Built with R version 4.4.0.
Wrangling site
data
The idea with this initial step is to define the scale of our
samples. Contributors to the NFA did not define their SiteID’s and
EventID’s similar to each other, so we’ll need to distinguish between
SiteID vs EventID and make sure it’s consistent throughout the data.
I define a SiteID as a unique place in space, i.e., all
samples from the same ‘beach’ (‘beach’ will be defined later on). An
EventID is a unique pull of the seine. So there can be single
or multiple EventID’s associated with a SiteID. Additionally, I
introduce a VisitID, which adds a temporal distinction among SiteID’s,
i.e., all events from the same beach on the same day.
Set up
Load required packages, define directory, set options, source
scripts:
# Packages
library(tidyverse)
library(lubridate)
library(here)
library(skimr)
library(sf)
library(DT)
# Directory
wd = here()
dirs = wd %>% list.files() %>% str_subset(pattern = "^README|^LICENSE|.md$|.Rproj$", negate = TRUE)
for (i in seq_along(dirs)) {
name = str_replace_all(dirs[i], "^", "dir.")
path = str_replace_all(dirs[i], "^", str_c(wd, "/"))
assign(name, path)
rm(name, path, i)
}
# Options
# Source
Read in data
data = read_csv(file.path(dir.data, "Raw Fish Atlas Sites 2024-10-16.csv"),
show_col_types = FALSE)
GearLookup = read_csv(file.path(dir.data, "FishAtlasExpansion_040422_Lookup_Gear.csv"),
show_col_types = FALSE)
Splitting site and
species info
As in typical community data (site x species), we have info tied to
site description and info tied to taxa. Notice that EventID is the
‘look-up’ key used in both data sets.
events.1 = data %>%
select(SiteID, EventID,
Date,
Lat, Lon = Long, Region, Location,
Habitat, TidalStage, Temp_C, Salinity,
GearSpecific, ProjectName, PointOfContact) %>%
mutate(Date = mdy(Date)) %>% # re-format date
distinct()
glimpse(events.1)
## Rows: 4,473
## Columns: 14
## $ SiteID <dbl> 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 4…
## $ EventID <dbl> 15, 60, 9, 89, 433, 634, 635, 767, 90, 12, 61, 16, 17, …
## $ Date <date> 2001-07-24, 2002-03-25, 2001-04-26, 2002-07-24, 2003-0…
## $ Lat <dbl> 58.57520, 58.57520, 58.57520, 58.57520, 58.57520, 58.57…
## $ Lon <dbl> -134.9263, -134.9263, -134.9263, -134.9263, -134.9263, …
## $ Region <chr> "Gulf of Alaska", "Gulf of Alaska", "Gulf of Alaska", "…
## $ Location <chr> "southeastern Alaska, Lynn Canal, North Island", "south…
## $ Habitat <chr> "Kelp", "Kelp", "Kelp", "Kelp", "Kelp", "Kelp", "Kelp",…
## $ TidalStage <chr> "LOW", "LOW", "LOW", "LOW", "LOW", "LOW", "LOW", "LOW",…
## $ Temp_C <dbl> 13.0, 4.0, 6.0, 13.0, 5.0, 12.0, 12.0, 3.7, 13.0, 6.0, …
## $ Salinity <dbl> 21, 33, 33, 18, 31, 16, 16, 33, 18, 33, 33, 21, 21, 33,…
## $ GearSpecific <chr> "BSEINE-STD", "BSEINE-STD", "BSEINE-STD", "BSEINE-STD",…
## $ ProjectName <chr> "SYNTHESIS", "SYNTHESIS", "SYNTHESIS", "SYNTHESIS", "SY…
## $ PointOfContact <chr> "NMFS, Alaska Fisheries Science Center, Auke Bay Labora…
catch.1 = data %>%
select(EventID,
Sp_CommonName, Sp_ScientificName, Fam_CommonName, Fam_ScientificName,
Unmeasured, Length_mm, LengthType, LifeStage)
glimpse(catch.1)
## Rows: 161,621
## Columns: 9
## $ EventID <dbl> 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,…
## $ Sp_CommonName <chr> "Coho salmon", "Great sculpin", "Great sculpin", "P…
## $ Sp_ScientificName <chr> "Oncorhynchus kisutch", "Myoxocephalus polyacanthoc…
## $ Fam_CommonName <chr> "trouts and salmons", "sculpins", "sculpins", "cods…
## $ Fam_ScientificName <chr> "Salmonidae", "Cottidae", "Cottidae", "Gadidae", "P…
## $ Unmeasured <dbl> 0, 0, 0, 0, 0, 10, 3, 1, 4, 0, 0, 0, 0, 0, 0, 0, 0,…
## $ Length_mm <dbl> 157, 208, 167, 81, 153, NA, NA, NA, NA, 66, 50, 53,…
## $ LengthType <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
## $ LifeStage <chr> "JUV", "JUV", "JUV", "YOY", "JUV", "JUV", NA, NA, N…
Considering gear
type
The NFA website makes it easy to filter for gear type, which I did
prior to downloading the .csv file. However, we’ll want to account for
differences among the beach seines used- namely the net mesh size. This
information is contained in the metadata for the NFA database and can be
accessed by request from the database managers (file
FishAtlasExpansion_040422_Lookup_Gear.csv). A few instances of
missing mesh size information was provided by email request to the
associated point of contact for that contributed data. Results from the
below code are hidden but comments explain what decisions were made.
# Join the mesh size info from our gear data to the site information
gear.1 = left_join(events.1, select(GearLookup, GearSpecific, MeshSize), by = "GearSpecific")
# Subset for gear related info and take a look
gear.2 = select(gear.1, EventID, MeshSize, GearSpecific, ProjectName, PointOfContact)
glimpse(gear.2)
# How many different mesh sizes are there?
gear.2$MeshSize %>% unique()
# Looks like we have an NA to take care of...
filter(gear.2, is.na(MeshSize))
# These are all from the GOAIERP project (PI Olav Ormseth). The report from that project reports 6 mm stretched mesh.
# We'll just go ahead and fix it here instead of changing the GearLookup file,
gear.3 = mutate(gear.2, MeshSize = ifelse(is.na(MeshSize), 6, MeshSize))
# Check our mesh sizes again,
gear.3$MeshSize %>% unique()
# Great, no more NA's.
# We can now join this information with our site data,
events.2 = left_join(events.1, select(gear.3, EventID, MeshSize), by = "EventID")
The “Site-Event”
problem
The catch data is tied to unique EventID’s which are associated with
spatially explicit SiteID’s (each SiteID has a unique lat/lon).
Considering that the data has been contributed by multiple
researchers/projects, we’ll need to make sure we have consistent sites
and events across the whole dataset. After that we can tackle the catch
data. First, we take a look at all of the site data with
skimr::skim().
skim(events.2)
Data summary
| Name |
events.2 |
| Number of rows |
4473 |
| Number of columns |
15 |
| _______________________ |
|
| Column type frequency: |
|
| character |
7 |
| Date |
1 |
| numeric |
7 |
| ________________________ |
|
| Group variables |
None |
Variable type: character
| Region |
0 |
1.00 |
10 |
16 |
0 |
5 |
0 |
| Location |
1 |
1.00 |
8 |
135 |
0 |
406 |
0 |
| Habitat |
0 |
1.00 |
4 |
13 |
0 |
8 |
0 |
| TidalStage |
1007 |
0.77 |
3 |
5 |
0 |
6 |
0 |
| GearSpecific |
0 |
1.00 |
8 |
12 |
0 |
15 |
0 |
| ProjectName |
0 |
1.00 |
4 |
31 |
0 |
31 |
0 |
| PointOfContact |
0 |
1.00 |
91 |
151 |
0 |
11 |
0 |
Variable type: Date
| Date |
0 |
1 |
1976-05-29 |
2021-09-09 |
2006-04-25 |
1035 |
Variable type: numeric
| SiteID |
0 |
1.00 |
1173.39 |
597.51 |
1.00 |
725.00 |
1091.00 |
1664.00 |
2505.00 |
▅▃▇▇▁ |
| EventID |
0 |
1.00 |
6345.33 |
3081.50 |
1.00 |
3613.00 |
6988.00 |
9176.00 |
10340.00 |
▃▃▁▇▇ |
| Lat |
0 |
1.00 |
59.66 |
3.58 |
51.64 |
58.33 |
59.40 |
59.60 |
71.39 |
▁▇▃▁▁ |
| Lon |
0 |
1.00 |
-145.21 |
9.86 |
-176.95 |
-151.63 |
-150.22 |
-134.95 |
-130.99 |
▁▁▇▁▇ |
| Temp_C |
1966 |
0.56 |
9.97 |
3.45 |
-0.92 |
7.40 |
10.50 |
12.50 |
28.70 |
▂▇▇▁▁ |
| Salinity |
1988 |
0.56 |
24.07 |
14.20 |
-400.00 |
19.80 |
25.11 |
30.10 |
73.50 |
▁▁▁▁▇ |
| MeshSize |
0 |
1.00 |
5.36 |
3.52 |
3.00 |
3.00 |
3.20 |
6.00 |
12.70 |
▇▂▁▁▂ |
In particular we want to get a sense of how EventIDs are associated
with SiteIDs and other variables placing them in space and time.


We see a lot of single-pull samples when events are grouped by SiteID
and Date, i.e., a visit. However, we can’t assume all of those
‘visits’ are comparable in scope. Projects differed in structure and
purpose, so what one researcher would consider a new site, another
researcher might call a replicate event to be combined with other events
within a single sample. I call this the “Site-Event” problem.
We see that the distribution of events becomes less severe when
grouped by Date and Location, which tells me that some of those
single-pull samples actually should be aggregated to be comparable to
other multi-pull samples. However, grouping by location may not be
accurate because of the loose definition of ‘Location’ (the levels of
Location also seem to vary in scale). Since location info is tied to
SiteID, we can’t just look at EventID’s and lat/lon. Instead, we’ll need
to figure out a way to combine events at the SiteID level. Basically, we
are trying to combine SiteID’s that are currently unique but should have
the same identifier, i.e., samples of the same beach.
Solving the
“Site-Event” problem
Here is where we need to decide on how to define a ‘beach’. In other
words, what do we consider the spatial scale distinguishing one sample
from another? From my personal seining, one of my larger-distanced sites
was near Anchor Point, AK, where we’ve seined up to 800 m between first
and last replicate. So in my opinion, 1 km would be reasonably
conservative estimate to link replicate seines. Imagine seining by
skiff- you could hop between replicates ~1 km down beach and still
consider it the same site. In the below code, I define clusters of
SiteID’s by grouping points that are within 1000 m of at least one other
SiteID.
# Make a new tibble containing SiteID geometries:
sites.sf = events.2 %>%
select(SiteID, Lat, Lon) %>%
# Create sf object from lat/lon:
st_as_sf(., coords = c("Lon", "Lat"), crs = 4326) %>% # WGS84
# Project into Alaska Albers 3338
st_transform(crs = 3338) %>%
distinct()
# Define a buffer of 1km around each point
sites.sf.buf = st_buffer(sites.sf, dist = 1000)
# Create a tibble with clusters of overlapping buffer areas
sites.sf.cl = sites.sf.buf %>%
st_union() %>% # unite buffer areas
st_cast(to = "POLYGON") %>% # turn them into polygon features
st_as_sf() %>% # return geometry set features to sfc
rownames_to_column(var = "Cluster") %>% # create col for cluster groups
mutate(Cluster = as.factor(Cluster)) # use factor class
# Assign SiteID points to buffer area clusters
sites.sf.cl.join = st_join(sites.sf, sites.sf.cl, left = TRUE)
# Drop sf class from joined data
sites.cl = st_drop_geometry(sites.sf.cl.join)
# See what we got:
glimpse(sites.cl)
## Rows: 1,178
## Columns: 2
## $ SiteID <dbl> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,…
## $ Cluster <fct> 327, 327, 327, 327, 327, 326, 326, 326, 326, 327, 327, 327, 32…
We now have a simple classification variable that can be joined back
with the site information.
events.3 = left_join(events.2, sites.cl, by = "SiteID")
However, this only takes into account the spatial component of our
larger goal of creating a unique VisitID identifier. Next, we’ll account
for the sample dates where SiteID’s within a cluster match.
# Create a date-cluster tibble containing location and event info to be combined,
orig = select(events.3, Date, Cluster, SiteID, EventID, Lat, Lon)
# Nest the df by Date and Cluster,
orig.nest = orig %>%
group_by(Date, Cluster) %>%
nest(Sites = SiteID,
Events = EventID,
Lats = Lat,
Lons = Lon) %>%
ungroup()
We essentially created a our new sample identifier (visit) in nesting
by date (day) and cluster (beach). To bring the data back to the EventID
level, we need to deal with samples that now have multiple SiteID’s and
locations, as well as preserve the EventID’s associated with the
original SiteID. This means we will be overwriting data, which is why I
called the above subset ‘orig’ and the below ‘new’.
new = orig.nest %>%
rowwise() %>% # Operates a row-at-a-time (per sample)
mutate(SiteID.cl = min(Sites),
Lat.mean = mean(Lats$Lat),
Lon.mean = mean(Lons$Lon)) %>%
select(-c(Sites, Lats, Lons)) %>% # Remove original info in nested variables
unite(col = VisitID, SiteID.cl, Date, sep = "_", remove = FALSE) %>% # create VisitID
select(-c(Date, SiteID.cl, Cluster)) %>% # Remove info now captured by VisitID
unnest_longer(Events) %>% # Unnest to the EventID level
unpack(cols = Events) # change tibble back to vector
# Join our new identifier to the rest of the site data,
events = left_join(new, events.3, by = "EventID") %>%
mutate(Lat = Lat.mean, Lon = Lon.mean) %>% # Replace old lat/lon with new mean lat/lon
select(-c(Lat.mean, Lon.mean))
# Check our our new data structure
head(events, 12)
## # A tibble: 12 × 17
## VisitID EventID SiteID Date Lat Lon Region Location Habitat
## <chr> <dbl> <dbl> <date> <dbl> <dbl> <chr> <chr> <chr>
## 1 1_2001-07-24 15 1 2001-07-24 58.6 -135. Gulf of … southea… Kelp
## 2 1_2001-07-24 16 2 2001-07-24 58.6 -135. Gulf of … southea… Kelp
## 3 1_2001-07-24 17 3 2001-07-24 58.6 -135. Gulf of … southea… Kelp
## 4 1_2001-07-24 18 4 2001-07-24 58.6 -135. Gulf of … southea… Sand-G…
## 5 1_2001-07-24 19 5 2001-07-24 58.6 -135. Gulf of … southea… Bedrock
## 6 1_2002-03-25 60 1 2002-03-25 58.6 -135. Gulf of … southea… Kelp
## 7 1_2002-03-25 61 2 2002-03-25 58.6 -135. Gulf of … southea… Kelp
## 8 1_2002-03-25 62 3 2002-03-25 58.6 -135. Gulf of … southea… Kelp
## 9 1_2002-03-25 63 4 2002-03-25 58.6 -135. Gulf of … southea… Sand-G…
## 10 1_2002-03-25 64 5 2002-03-25 58.6 -135. Gulf of … southea… Bedrock
## 11 1_2002-03-25 69 10 2002-03-25 58.6 -135. Gulf of … southea… Sand-G…
## 12 1_2002-03-25 70 11 2002-03-25 58.6 -135. Gulf of … southea… Sand-G…
## # ℹ 8 more variables: TidalStage <chr>, Temp_C <dbl>, Salinity <dbl>,
## # GearSpecific <chr>, ProjectName <chr>, PointOfContact <chr>,
## # MeshSize <dbl>, Cluster <fct>
We can see in first dozen rows how the locations now structured by
VisitID compares to the original based on SiteID. To select an
identifier for the new VisitID, I simply went with the lowest SiteID of
the nest list (there were no wrong answers here). The lat/lon’s to be
combined are at most a handful kms apart, so I figure a simple mean
would suffice. Notice we have unique lat/lon’s at the VisitID level-
this is because different visits to the same beach might have had a
different number of replicates. For example, we see that VisitID
1_2001-07-24 involved the first five events (EventID’s 15-19), whereas,
VisitID 1_2002-03-25 had an additional two events.
OK, great. Lastly, let’s return to our frequency plots showing the
number events per sample:


Wow. There are way less single-pull samples! Also, there are some
interesting samples with replicates of 9, 10, and 12. Some stats on the
number of events per visit:
mean = 2.53
median = 2
sd = 1.76
Clean up
# Clean up our environment for the next step:
rm(list = ls()[!ls() %in% c('events', 'catch.1', 'data')])
# Reset directory
wd = here()
dirs = wd %>% list.files() %>% str_subset(pattern = "^README|^LICENSE|.md$|.Rproj$", negate = TRUE)
for (i in seq_along(dirs)) {
name = str_replace_all(dirs[i], "^", "dir.")
path = str_replace_all(dirs[i], "^", str_c(wd, "/"))
assign(name, path)
rm(path, i)
}
Wrangling catch
data
Similar to our wrangle of site information, we want to hone the
breadth of the catch data we use. At this stage, we don’t want to make
any interpretations about the catch. Instead, the goal here is
to weed out objectively unneeded and suspicious cases and ensure the
consistency of the data (e.g., are all possible species named actual
names?).
Skimming catch
skim(catch.1)
Data summary
| Name |
catch.1 |
| Number of rows |
161621 |
| Number of columns |
9 |
| _______________________ |
|
| Column type frequency: |
|
| character |
6 |
| numeric |
3 |
| ________________________ |
|
| Group variables |
None |
Variable type: character
| Sp_CommonName |
0 |
1.00 |
7 |
31 |
0 |
195 |
0 |
| Sp_ScientificName |
0 |
1.00 |
7 |
38 |
0 |
190 |
0 |
| Fam_CommonName |
9 |
1.00 |
4 |
18 |
0 |
34 |
0 |
| Fam_ScientificName |
9 |
1.00 |
7 |
18 |
0 |
34 |
0 |
| LengthType |
77836 |
0.52 |
2 |
6 |
0 |
7 |
0 |
| LifeStage |
90497 |
0.44 |
3 |
8 |
0 |
6 |
0 |
Variable type: numeric
| EventID |
0 |
1.00 |
6017.42 |
3244.78 |
1 |
3199 |
6618 |
9192 |
10340 |
▂▆▁▃▇ |
| Unmeasured |
0 |
1.00 |
12.80 |
512.01 |
0 |
0 |
0 |
0 |
99997 |
▇▁▁▁▁ |
| Length_mm |
11535 |
0.93 |
98.33 |
74.16 |
4 |
48 |
76 |
124 |
930 |
▇▁▁▁▁ |
One nice thing about skim() is that it quickly tells
you where the information is missing and how much is missing. We see
that there are nine cases of missing data at the family level. Something
else that jumps out to me is that the number of unique species common
names is not the same as the number of unique species scientific
names.
I’ll also change the ‘Unmeasured’ variable to a count abundance,
which may not be totally necessary but will make it easier for me to
work with the data.
Another thing to point out is that less than half of the data appears
to have life stage information. Furthermore, the data with length
information is a little over 50%. We expect some missing lengths because
most projects will measure size for a subset of each species and count
the rest. Typically, I expect 20-50 individuals sized before a row
indicated the unmeasured count, which would result in a very low
percentage of “missing” lengths (maybe 5% or less?). But missing lengths
over 50% indicate some projects just didn’t measure their catch, which
is a shame because we could have inferred life stage from length. At
this point, it doesn’t seem worth while to toss out potentially half the
data to include either of those variables. Although, I may return to
this down the road.
First, let’s check out all the taxa we have in the data.
Editing taxonomy
The discrepancy between species common and scientific names has to do
with the cases where fish were given unidentified or juvenile labels.
Nothing to fix here but we’ll just make sure to use scientific names in
analyses.
I also notice a couple of cases where species are called something
‘or’ something else:
- Anisarchus medius or Lumpenus fabricii
- Osmeridae or Clupidae (misspelled)
Looking at the family table, we don’t see ‘Osmeridae or Clupeidae’,
so I think that replacing these ‘or’ cases with their associated family
scientific name should suffice. Speaking of the family table, I see that
those missing data show up as NA’s. We’ll take a look at it and make a
judgment call on how to deal with them.
Last, I’ll also create a genus classification, which could end up
being the level at which we do the analyses, depending on what that
looks like.
I do not show results of the below code, but I provided an edited
taxa list and see also comments for steps taken.
# Check out the cases where family is missing
filter(catch.1, is.na(Fam_ScientificName))
# Look up the visit info for these cases
filter(events, EventID %in% catch.1$EventID[which(is.na(catch.1$Fam_ScientificName))])
# Check out everything else caught during these visits
filter(catch.1, EventID %in% catch.1$EventID[which(is.na(catch.1$Fam_ScientificName))])
# Since these nine NAs make up a small portion of their samples,
# The simplest solve here is to just remove the data.
# Create a new object after edits made to taxonomy
catch.2 = catch.1 %>%
# Drop the nine instances of NA catch
drop_na(Fam_ScientificName) %>%
# Find the 'or' species and replace with Fam_ScientificName value
mutate(Sp_ScientificName = ifelse(str_detect(Sp_ScientificName, pattern = " or "),
Fam_ScientificName,
Sp_ScientificName),
# Create genus class by taking first word of Sp_ScientificName
Gen_ScientificName = word(Sp_ScientificName, 1))
Replacing
‘Unmeasured’ with ‘Count’
The below code changes the ‘Unmeasured’ parameter into ‘Count’
abundance:
catch.3 = mutate(catch.2, Count = ifelse(Unmeasured == 0, 1, Unmeasured)) %>%
select(-Unmeasured) # remove unused parameter
Next, I’ll make graphs and tables describing the abundance and
occurrence of our catch:
Visualizing catch
Family


Genus


Species


Defining rare
taxa
Removing rare species is common when dealing with community data
because of the affect that the most rare species (outliers) would have
in typical analyses to be conducted (e.g., analysis of variance).
Depending on our research questions, we may not consider rare cases
functional members of the community- in these cases it may make more
sense to view the community as those taxa most well represented. But if
I were more interested in richness, then I would want to conservatively
retain rare taxa.
I think we can convince ourselves that removing single count and even
single occurence species would be reasonable, because we do not have any
information to let us know whether those cases represent an actual
community member or whether they represent some sort of sampling error
(e.g., mistakenly labelling all the salmonids in a particular sample
Atlantic Salmon rather than a Pacific). Our research questions broadly
deal with spatial and temporal distributions of community structure, so
I think a ‘middle of the road’ approach is how we’ll want to proceed (at
least for now).
We’ll want to be objective in how we define rare taxa. To me, it
makes sense to consider occurrence rather than abundance. Our decision
should balance the information lost vs retained, and consider how it
affects each level of taxonomy. However, I do not know what taxa level
makes most sense for future analyses at this point so we’ll want to keep
our options open. For now, let’s consider it “rare” when taxa appear in
less than 5 total visits (less than 0.25% percent occurrence). With
that, removing rare taxa causes:
17.6% reduction in richness at the Family level (6 of
34)
25.6% reduction in richness at the Genus level (30 of
117)
31.4% reduction in richness at the Species Level (59 of
188)
Note that this fixed cut-off affects species diversity more so than
family diversity. Some of this can be explained by the fact that the
species list contains many identifications at the genus and family
level, which makes sense since identification often includes cases where
researchers aggregate ‘iffy’ ID’s into a higher class. I expect this
issue to be especially apparent in a collaborative dataset like the
NFA.
Lists of rare taxa
Species
Cool- now let’s re-visit our abundance and occurrence graphs to see
how they look:
Averaging abundance
per visit
Because our samples (visits) contain a variable number of replicates
(events), we should use an averaging method to calculate abundance.
Here, we’ll use a simple mean (i.e., total count / number of
replicates). This is our first instance of data standardization, which
may be further standardized or transformed depending on next steps
analyses. Also, at some point in the future it’d be neat to use the size
variable to bin our taxa by length. But for now, we’ll move forward
simply using the abundance data (minus rare taxa).
## Family abundance per visit
# Save an object of all the rare families
rare.families = unique(rare.fam$Fam_ScientificName) %>% as.character()
# Create a tibble of family abundance excluding rare families
catch.fam = left_join(select(events, VisitID, EventID), catch.3, by = "EventID") %>%
select(VisitID,
EventID,
Fam_ScientificName,
Count) %>%
filter(!Fam_ScientificName %in% rare.families) %>% # remove rare taxa
summarise(Abundance = sum(Count) / n_distinct(EventID), .by = c(VisitID, Fam_ScientificName))
## Genus abundance per visit
# Save an object of all the rare genus
rare.genus = unique(rare.gen$Gen_ScientificName) %>% as.character()
# Create a tibble of genus abundance excluding rare genus
catch.gen = left_join(select(events, VisitID, EventID), catch.3, by = "EventID") %>%
select(VisitID,
EventID,
Gen_ScientificName,
Count) %>%
filter(!Gen_ScientificName %in% rare.genus) %>% # remove rare taxa
ungroup() %>%
summarise(Abundance = (sum(Count) / n_distinct(EventID)), .by=c(VisitID, Gen_ScientificName))
## Species abundance per visit
# Save an object of all the rare species
rare.species = unique(rare.sp$Sp_ScientificName) %>% as.character()
# Create a tibble of species abundance excluding rare species
catch.sp = left_join(select(events, VisitID, EventID), catch.3, by = "EventID") %>%
select(VisitID,
EventID,
Sp_ScientificName,
Count) %>%
filter(!Sp_ScientificName %in% rare.species) %>% # remove rare taxa
ungroup() %>%
summarise(Abundance = (sum(Count) / n_distinct(EventID)), .by = c(VisitID, Sp_ScientificName))
Just by looking at the number of observations (rows) in the data, I
think we may end up conducting analyses at the genus level:
10275 observations at the family level
14093 observations at the genus level
15415 observations at the species level
Last thing we’ll do is rename our catch tibble and clean up our
environment.
# Re-assign working version of catch to a cleaner name
assign("catch", catch.3)
Clean up
# Clean up our environment for the next step:
rm(list = ls()[!ls() %in% c('data', 'events', 'catch', 'catch.fam', 'catch.gen', 'catch.sp', 'rare.families', 'rare.genus', 'rare.species')])
# Reset directory
wd = here()
dirs = wd %>% list.files() %>% str_subset(pattern = "^README|^LICENSE|.md$|.Rproj$", negate = TRUE)
for (i in seq_along(dirs)) {
name = str_replace_all(dirs[i], "^", "dir.")
path = str_replace_all(dirs[i], "^", str_c(wd, "/"))
assign(name, path)
rm(path, i)
}
Wrangling visit
data
Set up
Remember that our samples operate at the visit level, where unique
events represent replicates within each sample. Let’s create a new
tibble where each row represents a sample. We’ll want to keep track of
how many replicates each sample contains so that we may account for this
in future analyses.
# New tibble for visit level wrangling,
visits.1 = select(events, VisitID, EventID) %>%
summarise(Replicates = n_distinct(EventID), .by = VisitID) %>%
right_join(select(events, -SiteID, -EventID), by = "VisitID") %>%
distinct()
visits.1 %>% nrow()
## [1] 3034
Skimming visits
There are supposed to be 1770 visits, but we see more rows than that
in our dataset. When we merged sites together, there must’ve been
samples that contained mutliple entries for some of our variables (e.g.,
more than one Location or Habitat). Let’s skim the data to remind
ourselves of our variables:
skim(visits.1)
Data summary
| Name |
visits.1 |
| Number of rows |
3034 |
| Number of columns |
16 |
| _______________________ |
|
| Column type frequency: |
|
| character |
8 |
| Date |
1 |
| factor |
1 |
| numeric |
6 |
| ________________________ |
|
| Group variables |
None |
Variable type: character
| VisitID |
0 |
1.00 |
12 |
15 |
0 |
1770 |
0 |
| Region |
0 |
1.00 |
10 |
16 |
0 |
5 |
0 |
| Location |
1 |
1.00 |
8 |
135 |
0 |
406 |
0 |
| Habitat |
0 |
1.00 |
4 |
13 |
0 |
8 |
0 |
| TidalStage |
694 |
0.77 |
3 |
5 |
0 |
6 |
0 |
| GearSpecific |
0 |
1.00 |
8 |
12 |
0 |
15 |
0 |
| ProjectName |
0 |
1.00 |
4 |
31 |
0 |
31 |
0 |
| PointOfContact |
0 |
1.00 |
91 |
151 |
0 |
11 |
0 |
Variable type: Date
| Date |
0 |
1 |
1976-05-29 |
2021-09-09 |
2006-06-14 |
1035 |
Variable type: factor
| Cluster |
0 |
1 |
FALSE |
436 |
140: 203, 143: 161, 136: 114, 348: 113 |
Variable type: numeric
| Replicates |
0 |
1.0 |
3.27 |
2.00 |
1.00 |
2.00 |
3.00 |
4.00 |
12.00 |
▇▂▂▁▁ |
| Lat |
0 |
1.0 |
59.91 |
4.07 |
51.64 |
58.30 |
59.42 |
59.61 |
71.39 |
▁▇▅▁▂ |
| Lon |
0 |
1.0 |
-145.37 |
9.93 |
-176.95 |
-151.71 |
-149.65 |
-134.95 |
-131.00 |
▁▁▇▁▇ |
| Temp_C |
1208 |
0.6 |
9.84 |
3.53 |
-0.92 |
7.31 |
10.35 |
12.47 |
28.70 |
▂▇▇▁▁ |
| Salinity |
1218 |
0.6 |
24.33 |
15.97 |
-400.00 |
20.18 |
26.00 |
30.35 |
73.50 |
▁▁▁▁▇ |
| MeshSize |
0 |
1.0 |
4.60 |
2.91 |
3.00 |
3.00 |
3.20 |
6.00 |
12.70 |
▇▁▁▁▁ |
Right away, we can rule out the variables that we’ve already dealt
with and know are unique at the visit level: VisitID, Lat/Lon, Date, and
Cluster.
We can also simply drop the information that we don’t need to keep in
this version of the data, namely ProjectName, PointOfContact, and Gear
Specific. I think we can also remove the TidalStage parameter, because I
don’t care to account for it at this point.
# Update tibble to remove unnecessary site info,
visits.2 = select(visits.1, -c(ProjectName, PointOfContact, GearSpecific, TidalStage)) %>% distinct()
visits.2 %>% nrow()
## [1] 2766
Still not quite there. I bet a lot of visits contain multiple temps,
salinity, habitats, and probably locations. We might want to use those
later on, but for now we can just drop them because all their info is
contained in our event level data object:
# Update tibble to remove 'problem' variables,
visits.3 = select(visits.2, -c(Region, Location, Habitat, Temperature = Temp_C, Salinity)) %>% distinct()
visits.3 %>% nrow()
## [1] 1867
Pretty darn close. Hopefully we don’t have samples with multiple mesh
sizes… (spoiler) we do. Yuck.
The multiple
gear-type problem
First, we isolate the cases where singular visits contain replicates
of differing mesh sizes:
# New object for visits with multiple mesh size
mult.mesh = select(visits.3, VisitID, MeshSize) %>%
summarise(`n Mesh Sizes` = n_distinct(MeshSize), .by = VisitID) %>%
filter(`n Mesh Sizes` > 1)
# View
mult.mesh
## # A tibble: 63 × 2
## VisitID `n Mesh Sizes`
## <chr> <int>
## 1 1083_2016-05-07 2
## 2 1083_2016-05-22 2
## 3 1083_2016-06-05 2
## 4 1083_2016-06-21 2
## 5 1083_2016-07-04 2
## 6 1083_2016-08-02 2
## 7 1083_2017-04-11 3
## 8 1083_2016-09-02 2
## 9 1083_2017-04-27 3
## 10 1083_2017-05-14 3
## # ℹ 53 more rows
Damn. Let’s get a better idea of how different these mesh sizes are,
and what data sets they come from:
# Join multiple mesh events with other event level info,
mult.mesh.events = select(mult.mesh, VisitID) %>% left_join(events, by = "VisitID")
# Skim the project and date info,
select(mult.mesh.events, GearSpecific, ProjectName, Date) %>%
mutate(across(c(GearSpecific, ProjectName), ~ as_factor(.x))) %>%
skim()
Data summary
| Name |
Piped data |
| Number of rows |
382 |
| Number of columns |
3 |
| _______________________ |
|
| Column type frequency: |
|
| Date |
1 |
| factor |
2 |
| ________________________ |
|
| Group variables |
None |
Variable type: Date
| Date |
0 |
1 |
2016-05-06 |
2017-09-07 |
2017-04-30 |
63 |
Variable type: factor
| GearSpecific |
0 |
1 |
FALSE |
3 |
BSE: 174, BSE: 166, BSE: 42 |
| ProjectName |
0 |
1 |
FALSE |
1 |
Gla: 382 |
# Summary of gear and mesh size per visit,
select(mult.mesh.events, VisitID, GearSpecific, MeshSize) %>%
summarise(n_visits = n_distinct(VisitID), .by = c(GearSpecific, MeshSize))
## # A tibble: 3 × 3
## GearSpecific MeshSize n_visits
## <chr> <dbl> <int>
## 1 BSEINE-B 9.5 62
## 2 BSEINE-W 12.7 63
## 3 BSEINE-small 6.4 35
The good thing is that I recognize these data. The visits that
contain different mesh size events come from the same project: EPSCoR
Fire & Ice Lynn Canal group in Southeast AK. They reported that
different mesh sizes had an effect on community composition using
side-by-side comparison of gear types in their methods (Lundstrom et
al. 2022). This explains why the cluster and grouping steps combined
the events.
The bad news is that we’ll need to re-do previous wrangling steps on
which mesh size is depoendent. This will involve separating the affected
VisitID’s into separate samples based on mesh size, which will result in
about 100 more unique VisitID’s and different mean abundance associated
catches.
We’ll need to start by updating our events level data object. Since
this involves overwriting a major data stage, I’ll be explicit in what
I’m doing:
# Create a tibble to store the VisitID's to be fixed
mult.mesh.fix = select(mult.mesh.events, VisitID, EventID, GearSpecific, MeshSize) %>%
# Create a unique suffix that identifies mesh size used per event
mutate(suffix = gsub("BSEINE-", "", GearSpecific)) %>%
# Add the suffix to VisitID, creating new VisitID's
unite(col = VisitID_new, VisitID, suffix, sep = "_") %>%
# Retain only the VisitID's and their EventID's
select(EventID, VisitID_new)
# Update the 'events' tibble with fixed VisitID's,
events = left_join(select(events, EventID, VisitID), mult.mesh.fix, by = "EventID") %>%
# Where VisitID's were not fixed, fill in with old VisitID's
mutate(VisitID_new = ifelse(is.na(VisitID_new), VisitID, VisitID_new)) %>%
# Remove old VisitID column
select(-VisitID) %>%
# Re-join events data (w/o the old VisitID's) with the new VisitID's,
left_join(select(events, -VisitID), by = c("EventID")) %>%
# Rename (this could be done earlier but seems clearer here)
rename(VisitID = VisitID_new)
Next, we need to re-run the catch and visits steps that are based on
the events data.
Re-creating catch
summaries
We’ll need to redo the abundance and occurrence calculations because
the number of visits has changed. Rare taxa will still be defined as
those occurring in less than 5 visits (still equivalent to <0.25%).
The frequency and abundance plots are not all that different, but I show
updated catch summary tables below for each taxa level. Note that I
highlight rare taxa in orange.
Re-calculating
abundance per visit
Similarly, we’ll want to to redo our visit mean abundances (taxa
total / # of events). The below code is pretty much the same as
previously run, except that I change the object name to better
distinguish this subset from other objects in our environment.
# Create a tibble of mean abundance per visit for each family excluding rare taxa
abun.fam = left_join(select(events, VisitID, EventID), catch, by = "EventID") %>%
select(VisitID,
EventID,
Fam_ScientificName,
Count) %>%
filter(!Fam_ScientificName %in% rare.families) %>%
ungroup() %>%
summarise(Abundance = (sum(Count) / n_distinct(EventID)), .by = c(VisitID, Fam_ScientificName))
# Create a tibble of mean abundance per visit for each Genus excluding rare taxa
abun.gen = left_join(select(events, VisitID, EventID), catch, by = "EventID") %>%
select(VisitID,
EventID,
Gen_ScientificName,
Count) %>%
filter(!Gen_ScientificName %in% rare.genus) %>% # remove rare taxa
ungroup() %>%
summarise(Abundance = (sum(Count) / n_distinct(EventID)), .by = c(VisitID, Gen_ScientificName))
# Create a tibble of mean abundance per visit for each Species excluding rare taxa
abun.sp = left_join(select(events, VisitID, EventID), catch, by = "EventID") %>%
select(VisitID,
EventID,
Sp_ScientificName,
Count) %>%
filter(!Sp_ScientificName %in% rare.species) %>% # remove rare taxa
ungroup() %>%
summarise(Abundance = (sum(Count) / n_distinct(EventID)), .by = c(VisitID, Sp_ScientificName))
Now we can re-run the VisitID wrangling code from earlier in this
section:
# Re-run visits.1 code,
visits.4 = select(events, VisitID, EventID) %>%
summarise(Replicates = n_distinct(EventID), .by = VisitID) %>%
right_join(select(events, -SiteID, -EventID), by = "VisitID") %>%
distinct() %>%
# Re-run visits.2 & visits.3 code,
select(-c(ProjectName, PointOfContact, GearSpecific, TidalStage,
Region, Location, Habitat, Temperature = Temp_C, Salinity)) %>%
distinct()
We should now have 1867 unique visits. Let’s double check that there
is only one row per VisitID:
n_distinct(visits.4$VisitID) == nrow(visits.4)
## [1] TRUE
Re-visiting sample
frequency
Let’s also review our events per visit frequency plot because that
will have changed too.

The difference isn’t very noticeable- we end up with a central
tendency that has slightly shifted left.
Last, we save our fixed visits tibble as a new object with a cleaner
name, and then clean up the environment for next steps.
visits = visits.4
LS0tCnRpdGxlOiAiTk9BQSBOZWFyc2hvcmUgRmlzaCBBdGxhcywgRGF0YSBXcmFuZ2xlIgphdXRob3I6ICJDaHJpcyBHdW8iCmRhdGU6ICJMYXN0IGNvbXBpbGVkIG9uIGByIGZvcm1hdChTeXMudGltZSgpLCAnJWQgJUIsICVZJylgIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogVFJVRQogICAgdG9jX2RlcHRoOiAyCiAgICB0b2NfZmxvYXQ6CiAgICAgIGNvbGxhcHNlZDogRkFMU0UKICAgICAgcHJpbnQ6IEZBTFNFCiAgICBudW1iZXJfc2VjdGlvbnM6IFRSVUUKICAgIGNvZGVfZG93bmxvYWQ6IFRSVUUKdGhlbWU6ICJmbGF0bHkiCi0tLQoKYGBge3IgaW5jbHVkZSA9IEZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQobWVzc2FnZSA9IEZBTFNFKQprbml0cjo6b3B0c19jaHVuayRzZXQod2FybmluZyA9IEZBTFNFKQprbml0cjo6b3B0c19jaHVuayRzZXQoc2l6ZSA9ICJzY3JpcHRzaXplIikKYGBgCgpCdWlsdCB3aXRoIFIgdmVyc2lvbiBgciBnZXRSdmVyc2lvbigpYC4KCiMgSW50cm9kdWN0aW9uCgpUaGUgcG9pbnQgb2YgdGhpcyBkb2N1bWVudCBpcyB0byBtYWtlIG9wZW4gYW5kIHNoYXJlLWFibGUgdGhlIHJlc2VhcmNoIG1ldGhvZHMgdXNlZCBmb3IgbXkgZGlzc2VydGF0aW9uIG9uIG5lYXJzaG9yZSBmaXNoIGNvbW11bml0aWVzIGluIEFsYXNrYSB3b3JraW5nIHRvd2FyZHMgYSBQaEQgaW4gbWFyaW5lIGJpb2xvZ3kgYXQgdGhlIFVuaXZlcnNpdHkgb2YgQWxhc2thIEZhaXJiYW5rcy4gSGVyZSwgSSBjb3ZlciBtb3N0IHN0ZXBzIG9mIHRoZSBpbml0aWFsIGRhdGEgcHJlcGFyYXRpb24gZm9yIG15IHNlY29uZCBhbmQgdGhpcmQgY2hhcHRlcnMgY29uY2VybmluZyBzcGF0aWFsIGFuZCB0ZW1wb3JhbCBkaXN0cmlidXRpb25zIG9mIG5lYXJzaG9yZSBmaXNoZXMgYWNyb3NzIHRoZSBzdGF0ZS4gVGhpcyBhbmQgb3RoZXIgcHJvamVjdCBmaWxlcyBjYW4gYmUgYWNjZXNzZWQgdmlhIHRoZSBLYWNoZW1hayBCYXkgTmF0aW9uYWwgRXN0dWFyaW5lIFJlc2VhcmNoIFJlc2VydmUncyBnaXRodWIgW25lYXJzaG9yZSBmaXNoIHJlcG9zaXRvcnldKGh0dHBzOi8vZ2l0aHViLmNvbS9rYm5lcnIvbmVhcnNob3JlLWZpc2gpLiBGb2xsb3dpbmcgdGhpcyBkb2N1bWVudCwgbmV4dCBzdGVwIGV4cGxvcmF0b3J5IGRhdGEgYW5hbHlzZXMgcmVmZXJpbmNpbmcgdGhlc2UgZGF0YSBjYW4gYmUgZm91bmQgYXQgW3RoaXMgUnB1YnMgc2l0ZV0oaHR0cHM6Ly9ycHVicy5jb20vY2hndW8xLzEyMzI2NzMpLgoKSW4gdGhpcyBkb2N1bWVudCBJIHNob3cgdGhlIHN0ZXBzIHRha2VuIGluIHdyYW5nbGluZyB0aGUgTk9BQSBOZWFyc2hvcmUgRmlzaCBBdGxhcyAoTkZBKSBkYXRhYmFzZS4gVGhpcyByZXBsYWNlcyB0aGUgc2VwYXJhdGUgc2NyaXB0cyAobmFtZWQgIi4vRmlzaEF0bGFzXF8jXF9kZXNjcmlwdGlvbi5SIikgYW5kIGNvbWJpbmVzIHRoZW0gaW50byBhIHNpbmdsZSwgc3RyZWFtbGluZWQgUiBtYXJrZG93biBmaWxlLiBUaGVzZSBSIHNjcmlwdHMgd2lsbCBiZSBhcmNoaXZlZCBpbiBjYXNlIEkgbmVlZCB0byByZXBsaWNhdGUgb2xkZXIgbWV0aG9kcy4gTmFtZWx5LCBJIHVzZWQgdG8gdXNlICoqcmdkYWwqKiBwYWNrYWdlIGZvciBzb21lIHN0ZXBzIGluIHRoZSB3cmFuZ2xpbmcgcHJvY2VzcyBidXQgdGhhdCBwYWNrYWdlIHN0b3BwZWQgYmVpbmcgc3VwcG9ydGVkIGNhLiBPY3QgMjAyMywgbW92aW5nIG15IHdvcmtmbG93IHRvIHRoZSAqKnNmKiogcGFja2FnZS4KCkxpbmsgdG8gdGhlIE5GQSBjYW4gYmUgZm91bmQgaGVyZSwgPGh0dHBzOi8vYWxhc2thZmlzaGVyaWVzLm5vYWEuZ292L21hcHBpbmcvc3ovPi4gVGhlIGVudGlyZSBkYXRhYmFzZSBjYW4gYmUgZG93bmxvYWRlZCBhcyBhIC5jc3YgZmlsZSwgd2hpY2ggaXMgd2hhdCBJIGFtIHVzaW5nLiBIb3dldmVyLCBJIGhhdmUgYWRkaXRpb25hbCBkYXRhIGZpbGVzIHRoYXQgd2VyZSBlaXRoZXIgc2hhcmVkIHdpdGggbWUgYnkgTkZBIGNvbnRlbnQgbWFuYWdlcnMgb3IgbW9kaWZpZWQgZnJvbSB0aGUgTk9BQSBORkEgZGF0YSBkaXJlY3RvcnkgZm9yIGVhc2Ugb2YgcmVhZGluZyBpbnRvIFIuIEkgd2lsbCB0cnkgdG8gc2ltcGxpZnkgdGhpcyBmb3Igb3RoZXJzIHRvIHJlcGxpY2F0ZSBpbiB0aGUgZnV0dXJlLgoKIyBXcmFuZ2xpbmcgc2l0ZSBkYXRhCgpUaGUgaWRlYSB3aXRoIHRoaXMgaW5pdGlhbCBzdGVwIGlzIHRvIGRlZmluZSB0aGUgc2NhbGUgb2Ygb3VyIHNhbXBsZXMuIENvbnRyaWJ1dG9ycyB0byB0aGUgTkZBIGRpZCBub3QgZGVmaW5lIHRoZWlyIFNpdGVJRCdzIGFuZCBFdmVudElEJ3Mgc2ltaWxhciB0byBlYWNoIG90aGVyLCBzbyB3ZSdsbCBuZWVkIHRvIGRpc3Rpbmd1aXNoIGJldHdlZW4gU2l0ZUlEIHZzIEV2ZW50SUQgYW5kIG1ha2Ugc3VyZSBpdCdzIGNvbnNpc3RlbnQgdGhyb3VnaG91dCB0aGUgZGF0YS4KCkkgZGVmaW5lIGEgU2l0ZUlEIGFzICphIHVuaXF1ZSBwbGFjZSBpbiBzcGFjZSosIGkuZS4sIGFsbCBzYW1wbGVzIGZyb20gdGhlIHNhbWUgJ2JlYWNoJyAoJ2JlYWNoJyB3aWxsIGJlIGRlZmluZWQgbGF0ZXIgb24pLiBBbiBFdmVudElEIGlzICphIHVuaXF1ZSBwdWxsIG9mIHRoZSBzZWluZSouIFNvIHRoZXJlIGNhbiBiZSBzaW5nbGUgb3IgbXVsdGlwbGUgRXZlbnRJRCdzIGFzc29jaWF0ZWQgd2l0aCBhIFNpdGVJRC4gQWRkaXRpb25hbGx5LCBJIGludHJvZHVjZSBhIFZpc2l0SUQsIHdoaWNoIGFkZHMgYSB0ZW1wb3JhbCBkaXN0aW5jdGlvbiBhbW9uZyBTaXRlSUQncywgaS5lLiwgKmFsbCBldmVudHMgZnJvbSB0aGUgc2FtZSBiZWFjaCBvbiB0aGUgc2FtZSBkYXkqLgoKIyMgU2V0IHVwCgpMb2FkIHJlcXVpcmVkIHBhY2thZ2VzLCBkZWZpbmUgZGlyZWN0b3J5LCBzZXQgb3B0aW9ucywgc291cmNlIHNjcmlwdHM6CgpgYGB7cn0KIyBQYWNrYWdlcwpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShsdWJyaWRhdGUpCmxpYnJhcnkoaGVyZSkKbGlicmFyeShza2ltcikKbGlicmFyeShzZikKbGlicmFyeShEVCkKCiMgRGlyZWN0b3J5CndkID0gaGVyZSgpCmRpcnMgPSB3ZCAlPiUgbGlzdC5maWxlcygpICU+JSBzdHJfc3Vic2V0KHBhdHRlcm4gPSAiXlJFQURNRXxeTElDRU5TRXwubWQkfC5ScHJvaiQiLCBuZWdhdGUgPSBUUlVFKQpmb3IgKGkgaW4gc2VxX2Fsb25nKGRpcnMpKSB7CiAgbmFtZSA9IHN0cl9yZXBsYWNlX2FsbChkaXJzW2ldLCAiXiIsICJkaXIuIikKICBwYXRoID0gc3RyX3JlcGxhY2VfYWxsKGRpcnNbaV0sICJeIiwgc3RyX2Mod2QsICIvIikpCiAgYXNzaWduKG5hbWUsIHBhdGgpCiAgcm0obmFtZSwgcGF0aCwgaSkKfQoKIyBPcHRpb25zCgojIFNvdXJjZQoKYGBgCgojIyBSZWFkIGluIGRhdGEKCmBgYHtyfQpkYXRhID0gcmVhZF9jc3YoZmlsZS5wYXRoKGRpci5kYXRhLCAiUmF3IEZpc2ggQXRsYXMgU2l0ZXMgMjAyNC0xMC0xNi5jc3YiKSwKICAgICAgICAgICAgICAgc2hvd19jb2xfdHlwZXMgPSBGQUxTRSkKR2Vhckxvb2t1cCA9IHJlYWRfY3N2KGZpbGUucGF0aChkaXIuZGF0YSwgIkZpc2hBdGxhc0V4cGFuc2lvbl8wNDA0MjJfTG9va3VwX0dlYXIuY3N2IiksCiAgICAgICAgICAgICAgICAgICAgICBzaG93X2NvbF90eXBlcyA9IEZBTFNFKQpgYGAKCiMjIFNwbGl0dGluZyBzaXRlIGFuZCBzcGVjaWVzIGluZm8KCkFzIGluIHR5cGljYWwgY29tbXVuaXR5IGRhdGEgKHNpdGUgeCBzcGVjaWVzKSwgd2UgaGF2ZSBpbmZvIHRpZWQgdG8gc2l0ZSBkZXNjcmlwdGlvbiBhbmQgaW5mbyB0aWVkIHRvIHRheGEuIE5vdGljZSB0aGF0IEV2ZW50SUQgaXMgdGhlICdsb29rLXVwJyBrZXkgdXNlZCBpbiBib3RoIGRhdGEgc2V0cy4KCmBgYHtyfQpldmVudHMuMSA9IGRhdGEgJT4lCiAgc2VsZWN0KFNpdGVJRCwgRXZlbnRJRCwKICAgICAgICAgRGF0ZSwKICAgICAgICAgTGF0LCBMb24gPSBMb25nLCBSZWdpb24sIExvY2F0aW9uLAogICAgICAgICBIYWJpdGF0LCBUaWRhbFN0YWdlLCBUZW1wX0MsIFNhbGluaXR5LAogICAgICAgICBHZWFyU3BlY2lmaWMsIFByb2plY3ROYW1lLCBQb2ludE9mQ29udGFjdCkgJT4lCiAgbXV0YXRlKERhdGUgPSBtZHkoRGF0ZSkpICU+JSAjIHJlLWZvcm1hdCBkYXRlCiAgZGlzdGluY3QoKQpnbGltcHNlKGV2ZW50cy4xKQoKY2F0Y2guMSA9IGRhdGEgJT4lCiAgc2VsZWN0KEV2ZW50SUQsCiAgICAgICAgIFNwX0NvbW1vbk5hbWUsIFNwX1NjaWVudGlmaWNOYW1lLCBGYW1fQ29tbW9uTmFtZSwgRmFtX1NjaWVudGlmaWNOYW1lLAogICAgICAgICBVbm1lYXN1cmVkLCBMZW5ndGhfbW0sIExlbmd0aFR5cGUsIExpZmVTdGFnZSkKZ2xpbXBzZShjYXRjaC4xKQpgYGAKCiMjIENvbnNpZGVyaW5nIGdlYXIgdHlwZQoKVGhlIE5GQSB3ZWJzaXRlIG1ha2VzIGl0IGVhc3kgdG8gZmlsdGVyIGZvciBnZWFyIHR5cGUsIHdoaWNoIEkgZGlkIHByaW9yIHRvIGRvd25sb2FkaW5nIHRoZSAuY3N2IGZpbGUuIEhvd2V2ZXIsIHdlJ2xsIHdhbnQgdG8gYWNjb3VudCBmb3IgZGlmZmVyZW5jZXMgYW1vbmcgdGhlIGJlYWNoIHNlaW5lcyB1c2VkLSBuYW1lbHkgdGhlIG5ldCBtZXNoIHNpemUuIFRoaXMgaW5mb3JtYXRpb24gaXMgY29udGFpbmVkIGluIHRoZSBtZXRhZGF0YSBmb3IgdGhlIE5GQSBkYXRhYmFzZSBhbmQgY2FuIGJlIGFjY2Vzc2VkIGJ5IHJlcXVlc3QgZnJvbSB0aGUgZGF0YWJhc2UgbWFuYWdlcnMgKGZpbGUgKkZpc2hBdGxhc0V4cGFuc2lvbl8wNDA0MjJfTG9va3VwX0dlYXIuY3N2KikuIEEgZmV3IGluc3RhbmNlcyBvZiBtaXNzaW5nIG1lc2ggc2l6ZSBpbmZvcm1hdGlvbiB3YXMgcHJvdmlkZWQgYnkgZW1haWwgcmVxdWVzdCB0byB0aGUgYXNzb2NpYXRlZCBwb2ludCBvZiBjb250YWN0IGZvciB0aGF0IGNvbnRyaWJ1dGVkIGRhdGEuIFJlc3VsdHMgZnJvbSB0aGUgYmVsb3cgY29kZSBhcmUgaGlkZGVuIGJ1dCBjb21tZW50cyBleHBsYWluIHdoYXQgZGVjaXNpb25zIHdlcmUgbWFkZS4KCmBgYHtyIHJlc3VsdHMgPSAnaGlkZSd9CiMgSm9pbiB0aGUgbWVzaCBzaXplIGluZm8gZnJvbSBvdXIgZ2VhciBkYXRhIHRvIHRoZSBzaXRlIGluZm9ybWF0aW9uCmdlYXIuMSA9IGxlZnRfam9pbihldmVudHMuMSwgc2VsZWN0KEdlYXJMb29rdXAsIEdlYXJTcGVjaWZpYywgTWVzaFNpemUpLCBieSA9ICJHZWFyU3BlY2lmaWMiKQoKIyBTdWJzZXQgZm9yIGdlYXIgcmVsYXRlZCBpbmZvIGFuZCB0YWtlIGEgbG9vawpnZWFyLjIgPSBzZWxlY3QoZ2Vhci4xLCBFdmVudElELCBNZXNoU2l6ZSwgR2VhclNwZWNpZmljLCBQcm9qZWN0TmFtZSwgUG9pbnRPZkNvbnRhY3QpCmdsaW1wc2UoZ2Vhci4yKQojIEhvdyBtYW55IGRpZmZlcmVudCBtZXNoIHNpemVzIGFyZSB0aGVyZT8KZ2Vhci4yJE1lc2hTaXplICU+JSB1bmlxdWUoKQoKIyBMb29rcyBsaWtlIHdlIGhhdmUgYW4gTkEgdG8gdGFrZSBjYXJlIG9mLi4uCmZpbHRlcihnZWFyLjIsIGlzLm5hKE1lc2hTaXplKSkKIyBUaGVzZSBhcmUgYWxsIGZyb20gdGhlIEdPQUlFUlAgcHJvamVjdCAoUEkgT2xhdiBPcm1zZXRoKS4gVGhlIHJlcG9ydCBmcm9tIHRoYXQgcHJvamVjdCByZXBvcnRzIDYgbW0gc3RyZXRjaGVkIG1lc2guCiMgV2UnbGwganVzdCBnbyBhaGVhZCBhbmQgZml4IGl0IGhlcmUgaW5zdGVhZCBvZiBjaGFuZ2luZyB0aGUgR2Vhckxvb2t1cCBmaWxlLApnZWFyLjMgPSBtdXRhdGUoZ2Vhci4yLCBNZXNoU2l6ZSA9IGlmZWxzZShpcy5uYShNZXNoU2l6ZSksIDYsIE1lc2hTaXplKSkKCiMgQ2hlY2sgb3VyIG1lc2ggc2l6ZXMgYWdhaW4sCmdlYXIuMyRNZXNoU2l6ZSAlPiUgdW5pcXVlKCkKIyBHcmVhdCwgbm8gbW9yZSBOQSdzLgoKIyBXZSBjYW4gbm93IGpvaW4gdGhpcyBpbmZvcm1hdGlvbiB3aXRoIG91ciBzaXRlIGRhdGEsCmV2ZW50cy4yID0gbGVmdF9qb2luKGV2ZW50cy4xLCBzZWxlY3QoZ2Vhci4zLCBFdmVudElELCBNZXNoU2l6ZSksIGJ5ID0gIkV2ZW50SUQiKQpgYGAKCiMjIFRoZSAiU2l0ZS1FdmVudCIgcHJvYmxlbQoKVGhlIGNhdGNoIGRhdGEgaXMgdGllZCB0byB1bmlxdWUgRXZlbnRJRCdzIHdoaWNoIGFyZSBhc3NvY2lhdGVkIHdpdGggc3BhdGlhbGx5IGV4cGxpY2l0IFNpdGVJRCdzIChlYWNoIFNpdGVJRCBoYXMgYSB1bmlxdWUgbGF0L2xvbikuIENvbnNpZGVyaW5nIHRoYXQgdGhlIGRhdGEgaGFzIGJlZW4gY29udHJpYnV0ZWQgYnkgbXVsdGlwbGUgcmVzZWFyY2hlcnMvcHJvamVjdHMsIHdlJ2xsIG5lZWQgdG8gbWFrZSBzdXJlIHdlIGhhdmUgY29uc2lzdGVudCBzaXRlcyBhbmQgZXZlbnRzIGFjcm9zcyB0aGUgd2hvbGUgZGF0YXNldC4gQWZ0ZXIgdGhhdCB3ZSBjYW4gdGFja2xlIHRoZSBjYXRjaCBkYXRhLiBGaXJzdCwgd2UgdGFrZSBhIGxvb2sgYXQgYWxsIG9mIHRoZSBzaXRlIGRhdGEgd2l0aCAqKnNraW1yOjpza2ltKCkqKi4KCmBgYHtyfQpza2ltKGV2ZW50cy4yKQpgYGAKCkluIHBhcnRpY3VsYXIgd2Ugd2FudCB0byBnZXQgYSBzZW5zZSBvZiBob3cgRXZlbnRJRHMgYXJlIGFzc29jaWF0ZWQgd2l0aCBTaXRlSURzIGFuZCBvdGhlciB2YXJpYWJsZXMgcGxhY2luZyB0aGVtIGluIHNwYWNlIGFuZCB0aW1lLgoKYGBge3IgZWNobyA9IEZBTFNFfQpldmVudHMuMiAlPiUgc3VtbWFyaXNlKEV2ZW50cyA9IG5fZGlzdGluY3QoRXZlbnRJRCksIC5ieSA9IGMoU2l0ZUlELCBEYXRlKSkgJT4lCiAgZ2dwbG90KGRhdGEgPSAuLCBhZXMoeCA9IEV2ZW50cykpICsKICBnZW9tX2JhcihhZXMoeSA9IGFmdGVyX3N0YXQoY291bnQpKSkgKwogIGdlb21fdGV4dChzdGF0ID0gJ0NvdW50JywgYWVzKGxhYmVsID0gYWZ0ZXJfc3RhdChjb3VudCkpLCB2anVzdCA9IC0wLjUpICsKICBsYWJzKHRpdGxlID0gIkZyZXF1ZW5jeSBvZiBldmVudHMgKHJlcGxpY2F0ZXMpIHBlciBTaXRlSUQtRGF0ZSBwYWlyIChzYW1wbGVzKSIpCgpldmVudHMuMiAlPiUgc3VtbWFyaXNlKEV2ZW50cyA9IG5fZGlzdGluY3QoRXZlbnRJRCksIC5ieSA9IGMoRGF0ZSwgTG9jYXRpb24pKSAlPiUKICBnZ3Bsb3QoZGF0YSA9IC4sIGFlcyh4ID0gRXZlbnRzKSkgKwogIGdlb21fYmFyKGFlcyh5ID0gYWZ0ZXJfc3RhdChjb3VudCkpKSArCiAgZ2VvbV90ZXh0KHN0YXQgPSAnQ291bnQnLCBhZXMobGFiZWwgPSBhZnRlcl9zdGF0KGNvdW50KSksIHZqdXN0ID0gLTAuNSkgKwogIGxhYnModGl0bGUgPSAiRnJlcXVlbmN5IG9mIGV2ZW50cyBwZXIgTG9jYXRpb24tRGF0ZSBwYWlyIikKYGBgCgpXZSBzZWUgYSBsb3Qgb2Ygc2luZ2xlLXB1bGwgc2FtcGxlcyB3aGVuIGV2ZW50cyBhcmUgZ3JvdXBlZCBieSBTaXRlSUQgYW5kIERhdGUsIGkuZS4sIGEgKnZpc2l0Ki4gSG93ZXZlciwgd2UgY2FuJ3QgYXNzdW1lIGFsbCBvZiB0aG9zZSAndmlzaXRzJyBhcmUgY29tcGFyYWJsZSBpbiBzY29wZS4gUHJvamVjdHMgZGlmZmVyZWQgaW4gc3RydWN0dXJlIGFuZCBwdXJwb3NlLCBzbyB3aGF0IG9uZSByZXNlYXJjaGVyIHdvdWxkIGNvbnNpZGVyIGEgbmV3IHNpdGUsIGFub3RoZXIgcmVzZWFyY2hlciBtaWdodCBjYWxsIGEgcmVwbGljYXRlIGV2ZW50IHRvIGJlIGNvbWJpbmVkIHdpdGggb3RoZXIgZXZlbnRzIHdpdGhpbiBhIHNpbmdsZSBzYW1wbGUuIEkgY2FsbCB0aGlzIHRoZSAiU2l0ZS1FdmVudCIgcHJvYmxlbS4KCldlIHNlZSB0aGF0IHRoZSBkaXN0cmlidXRpb24gb2YgZXZlbnRzIGJlY29tZXMgbGVzcyBzZXZlcmUgd2hlbiBncm91cGVkIGJ5IERhdGUgYW5kIExvY2F0aW9uLCB3aGljaCB0ZWxscyBtZSB0aGF0IHNvbWUgb2YgdGhvc2Ugc2luZ2xlLXB1bGwgc2FtcGxlcyBhY3R1YWxseSBzaG91bGQgYmUgYWdncmVnYXRlZCB0byBiZSBjb21wYXJhYmxlIHRvIG90aGVyIG11bHRpLXB1bGwgc2FtcGxlcy4gSG93ZXZlciwgZ3JvdXBpbmcgYnkgbG9jYXRpb24gbWF5IG5vdCBiZSBhY2N1cmF0ZSBiZWNhdXNlIG9mIHRoZSBsb29zZSBkZWZpbml0aW9uIG9mICdMb2NhdGlvbicgKHRoZSBsZXZlbHMgb2YgTG9jYXRpb24gYWxzbyBzZWVtIHRvIHZhcnkgaW4gc2NhbGUpLiBTaW5jZSBsb2NhdGlvbiBpbmZvIGlzIHRpZWQgdG8gU2l0ZUlELCB3ZSBjYW4ndCBqdXN0IGxvb2sgYXQgRXZlbnRJRCdzIGFuZCBsYXQvbG9uLiBJbnN0ZWFkLCB3ZSdsbCBuZWVkIHRvIGZpZ3VyZSBvdXQgYSB3YXkgdG8gY29tYmluZSBldmVudHMgYXQgdGhlIFNpdGVJRCBsZXZlbC4gQmFzaWNhbGx5LCB3ZSBhcmUgdHJ5aW5nIHRvIGNvbWJpbmUgU2l0ZUlEJ3MgdGhhdCBhcmUgY3VycmVudGx5IHVuaXF1ZSBidXQgc2hvdWxkIGhhdmUgdGhlIHNhbWUgaWRlbnRpZmllciwgaS5lLiwgc2FtcGxlcyBvZiB0aGUgc2FtZSBiZWFjaC4KCiMjIFNvbHZpbmcgdGhlICJTaXRlLUV2ZW50IiBwcm9ibGVtCgpIZXJlIGlzIHdoZXJlIHdlIG5lZWQgdG8gZGVjaWRlIG9uIGhvdyB0byBkZWZpbmUgYSAnYmVhY2gnLiBJbiBvdGhlciB3b3Jkcywgd2hhdCBkbyB3ZSBjb25zaWRlciB0aGUgc3BhdGlhbCBzY2FsZSBkaXN0aW5ndWlzaGluZyBvbmUgc2FtcGxlIGZyb20gYW5vdGhlcj8gRnJvbSBteSBwZXJzb25hbCBzZWluaW5nLCBvbmUgb2YgbXkgbGFyZ2VyLWRpc3RhbmNlZCBzaXRlcyB3YXMgbmVhciBBbmNob3IgUG9pbnQsIEFLLCB3aGVyZSB3ZSd2ZSBzZWluZWQgdXAgdG8gODAwIG0gYmV0d2VlbiBmaXJzdCBhbmQgbGFzdCByZXBsaWNhdGUuIFNvIGluIG15IG9waW5pb24sIDEga20gd291bGQgYmUgcmVhc29uYWJseSBjb25zZXJ2YXRpdmUgZXN0aW1hdGUgdG8gbGluayByZXBsaWNhdGUgc2VpbmVzLiBJbWFnaW5lIHNlaW5pbmcgYnkgc2tpZmYtIHlvdSBjb3VsZCBob3AgYmV0d2VlbiByZXBsaWNhdGVzIFx+MSBrbSBkb3duIGJlYWNoIGFuZCBzdGlsbCBjb25zaWRlciBpdCB0aGUgc2FtZSBzaXRlLiBJbiB0aGUgYmVsb3cgY29kZSwgSSBkZWZpbmUgY2x1c3RlcnMgb2YgU2l0ZUlEJ3MgYnkgZ3JvdXBpbmcgcG9pbnRzIHRoYXQgYXJlIHdpdGhpbiAxMDAwIG0gb2YgYXQgbGVhc3Qgb25lIG90aGVyIFNpdGVJRC4KCmBgYHtyfQojIE1ha2UgYSBuZXcgdGliYmxlIGNvbnRhaW5pbmcgU2l0ZUlEIGdlb21ldHJpZXM6CnNpdGVzLnNmID0gZXZlbnRzLjIgJT4lCiAgc2VsZWN0KFNpdGVJRCwgTGF0LCBMb24pICU+JQogICMgQ3JlYXRlIHNmIG9iamVjdCBmcm9tIGxhdC9sb246CiAgc3RfYXNfc2YoLiwgY29vcmRzID0gYygiTG9uIiwgIkxhdCIpLCBjcnMgPSA0MzI2KSAlPiUgIyBXR1M4NAogICMgUHJvamVjdCBpbnRvIEFsYXNrYSBBbGJlcnMgMzMzOAogIHN0X3RyYW5zZm9ybShjcnMgPSAzMzM4KSAlPiUKICBkaXN0aW5jdCgpCgojIERlZmluZSBhIGJ1ZmZlciBvZiAxa20gYXJvdW5kIGVhY2ggcG9pbnQKc2l0ZXMuc2YuYnVmID0gc3RfYnVmZmVyKHNpdGVzLnNmLCBkaXN0ID0gMTAwMCkKCiMgQ3JlYXRlIGEgdGliYmxlIHdpdGggY2x1c3RlcnMgb2Ygb3ZlcmxhcHBpbmcgYnVmZmVyIGFyZWFzIApzaXRlcy5zZi5jbCA9IHNpdGVzLnNmLmJ1ZiAlPiUKICBzdF91bmlvbigpICU+JSAjIHVuaXRlIGJ1ZmZlciBhcmVhcwogIHN0X2Nhc3QodG8gPSAiUE9MWUdPTiIpICU+JSAjIHR1cm4gdGhlbSBpbnRvIHBvbHlnb24gZmVhdHVyZXMKICBzdF9hc19zZigpICU+JSAjIHJldHVybiBnZW9tZXRyeSBzZXQgZmVhdHVyZXMgdG8gc2ZjCiAgcm93bmFtZXNfdG9fY29sdW1uKHZhciA9ICJDbHVzdGVyIikgJT4lICMgY3JlYXRlIGNvbCBmb3IgY2x1c3RlciBncm91cHMKICBtdXRhdGUoQ2x1c3RlciA9IGFzLmZhY3RvcihDbHVzdGVyKSkgIyB1c2UgZmFjdG9yIGNsYXNzCgojIEFzc2lnbiBTaXRlSUQgcG9pbnRzIHRvIGJ1ZmZlciBhcmVhIGNsdXN0ZXJzCnNpdGVzLnNmLmNsLmpvaW4gPSBzdF9qb2luKHNpdGVzLnNmLCBzaXRlcy5zZi5jbCwgbGVmdCA9IFRSVUUpCgojIERyb3Agc2YgY2xhc3MgZnJvbSBqb2luZWQgZGF0YQpzaXRlcy5jbCA9IHN0X2Ryb3BfZ2VvbWV0cnkoc2l0ZXMuc2YuY2wuam9pbikKCiMgU2VlIHdoYXQgd2UgZ290OgpnbGltcHNlKHNpdGVzLmNsKQpgYGAKCldlIG5vdyBoYXZlIGEgc2ltcGxlIGNsYXNzaWZpY2F0aW9uIHZhcmlhYmxlIHRoYXQgY2FuIGJlIGpvaW5lZCBiYWNrIHdpdGggdGhlIHNpdGUgaW5mb3JtYXRpb24uCgpgYGB7cn0KZXZlbnRzLjMgPSBsZWZ0X2pvaW4oZXZlbnRzLjIsIHNpdGVzLmNsLCBieSA9ICJTaXRlSUQiKQpgYGAKCkhvd2V2ZXIsIHRoaXMgb25seSB0YWtlcyBpbnRvIGFjY291bnQgdGhlIHNwYXRpYWwgY29tcG9uZW50IG9mIG91ciBsYXJnZXIgZ29hbCBvZiBjcmVhdGluZyBhIHVuaXF1ZSBWaXNpdElEIGlkZW50aWZpZXIuIE5leHQsIHdlJ2xsIGFjY291bnQgZm9yIHRoZSBzYW1wbGUgZGF0ZXMgd2hlcmUgU2l0ZUlEJ3Mgd2l0aGluIGEgY2x1c3RlciBtYXRjaC4KCmBgYHtyfQojIENyZWF0ZSBhIGRhdGUtY2x1c3RlciB0aWJibGUgY29udGFpbmluZyBsb2NhdGlvbiBhbmQgZXZlbnQgaW5mbyB0byBiZSBjb21iaW5lZCwKb3JpZyA9IHNlbGVjdChldmVudHMuMywgRGF0ZSwgQ2x1c3RlciwgU2l0ZUlELCBFdmVudElELCBMYXQsIExvbikKCiMgTmVzdCB0aGUgZGYgYnkgRGF0ZSBhbmQgQ2x1c3RlciwKb3JpZy5uZXN0ID0gb3JpZyAlPiUKICBncm91cF9ieShEYXRlLCBDbHVzdGVyKSAlPiUKICBuZXN0KFNpdGVzID0gU2l0ZUlELAogICAgICAgRXZlbnRzID0gRXZlbnRJRCwKICAgICAgIExhdHMgPSBMYXQsCiAgICAgICBMb25zID0gTG9uKSAlPiUKICB1bmdyb3VwKCkKYGBgCgpXZSBlc3NlbnRpYWxseSBjcmVhdGVkIGEgb3VyIG5ldyBzYW1wbGUgaWRlbnRpZmllciAodmlzaXQpIGluIG5lc3RpbmcgYnkgZGF0ZSAoZGF5KSBhbmQgY2x1c3RlciAoYmVhY2gpLiBUbyBicmluZyB0aGUgZGF0YSBiYWNrIHRvIHRoZSBFdmVudElEIGxldmVsLCB3ZSBuZWVkIHRvIGRlYWwgd2l0aCBzYW1wbGVzIHRoYXQgbm93IGhhdmUgbXVsdGlwbGUgU2l0ZUlEJ3MgYW5kIGxvY2F0aW9ucywgYXMgd2VsbCBhcyBwcmVzZXJ2ZSB0aGUgRXZlbnRJRCdzIGFzc29jaWF0ZWQgd2l0aCB0aGUgb3JpZ2luYWwgU2l0ZUlELiBUaGlzIG1lYW5zIHdlIHdpbGwgYmUgb3ZlcndyaXRpbmcgZGF0YSwgd2hpY2ggaXMgd2h5IEkgY2FsbGVkIHRoZSBhYm92ZSBzdWJzZXQgJ29yaWcnIGFuZCB0aGUgYmVsb3cgJ25ldycuCgpgYGB7cn0KbmV3ID0gb3JpZy5uZXN0ICU+JSAKICByb3d3aXNlKCkgJT4lICMgT3BlcmF0ZXMgYSByb3ctYXQtYS10aW1lIChwZXIgc2FtcGxlKQogIG11dGF0ZShTaXRlSUQuY2wgPSBtaW4oU2l0ZXMpLCAKICAgICAgICAgTGF0Lm1lYW4gPSBtZWFuKExhdHMkTGF0KSwKICAgICAgICAgTG9uLm1lYW4gPSBtZWFuKExvbnMkTG9uKSkgJT4lCiAgc2VsZWN0KC1jKFNpdGVzLCBMYXRzLCBMb25zKSkgJT4lICMgUmVtb3ZlIG9yaWdpbmFsIGluZm8gaW4gbmVzdGVkIHZhcmlhYmxlcwogIHVuaXRlKGNvbCA9IFZpc2l0SUQsIFNpdGVJRC5jbCwgRGF0ZSwgc2VwID0gIl8iLCByZW1vdmUgPSBGQUxTRSkgJT4lICMgY3JlYXRlIFZpc2l0SUQKICBzZWxlY3QoLWMoRGF0ZSwgU2l0ZUlELmNsLCBDbHVzdGVyKSkgJT4lICMgUmVtb3ZlIGluZm8gbm93IGNhcHR1cmVkIGJ5IFZpc2l0SUQKICB1bm5lc3RfbG9uZ2VyKEV2ZW50cykgJT4lICMgVW5uZXN0IHRvIHRoZSBFdmVudElEIGxldmVsCiAgdW5wYWNrKGNvbHMgPSBFdmVudHMpICMgY2hhbmdlIHRpYmJsZSBiYWNrIHRvIHZlY3RvcgoKIyBKb2luIG91ciBuZXcgaWRlbnRpZmllciB0byB0aGUgcmVzdCBvZiB0aGUgc2l0ZSBkYXRhLApldmVudHMgPSBsZWZ0X2pvaW4obmV3LCBldmVudHMuMywgYnkgPSAiRXZlbnRJRCIpICU+JQogIG11dGF0ZShMYXQgPSBMYXQubWVhbiwgTG9uID0gTG9uLm1lYW4pICU+JSAjIFJlcGxhY2Ugb2xkIGxhdC9sb24gd2l0aCBuZXcgbWVhbiBsYXQvbG9uCiAgc2VsZWN0KC1jKExhdC5tZWFuLCBMb24ubWVhbikpCgojIENoZWNrIG91ciBvdXIgbmV3IGRhdGEgc3RydWN0dXJlCmhlYWQoZXZlbnRzLCAxMikKYGBgCgpXZSBjYW4gc2VlIGluIGZpcnN0IGRvemVuIHJvd3MgaG93IHRoZSBsb2NhdGlvbnMgbm93IHN0cnVjdHVyZWQgYnkgVmlzaXRJRCBjb21wYXJlcyB0byB0aGUgb3JpZ2luYWwgYmFzZWQgb24gU2l0ZUlELiBUbyBzZWxlY3QgYW4gaWRlbnRpZmllciBmb3IgdGhlIG5ldyBWaXNpdElELCBJIHNpbXBseSB3ZW50IHdpdGggdGhlIGxvd2VzdCBTaXRlSUQgb2YgdGhlIG5lc3QgbGlzdCAodGhlcmUgd2VyZSBubyB3cm9uZyBhbnN3ZXJzIGhlcmUpLiBUaGUgbGF0L2xvbidzIHRvIGJlIGNvbWJpbmVkIGFyZSBhdCBtb3N0IGEgaGFuZGZ1bCBrbXMgYXBhcnQsIHNvIEkgZmlndXJlIGEgc2ltcGxlIG1lYW4gd291bGQgc3VmZmljZS4gTm90aWNlIHdlIGhhdmUgdW5pcXVlIGxhdC9sb24ncyBhdCB0aGUgVmlzaXRJRCBsZXZlbC0gdGhpcyBpcyBiZWNhdXNlIGRpZmZlcmVudCB2aXNpdHMgdG8gdGhlIHNhbWUgYmVhY2ggbWlnaHQgaGF2ZSBoYWQgYSBkaWZmZXJlbnQgbnVtYmVyIG9mIHJlcGxpY2F0ZXMuIEZvciBleGFtcGxlLCB3ZSBzZWUgdGhhdCBWaXNpdElEIDFfMjAwMS0wNy0yNCBpbnZvbHZlZCB0aGUgZmlyc3QgZml2ZSBldmVudHMgKEV2ZW50SUQncyAxNS0xOSksIHdoZXJlYXMsIFZpc2l0SUQgMV8yMDAyLTAzLTI1IGhhZCBhbiBhZGRpdGlvbmFsIHR3byBldmVudHMuCgpPSywgZ3JlYXQuIExhc3RseSwgbGV0J3MgcmV0dXJuIHRvIG91ciBmcmVxdWVuY3kgcGxvdHMgc2hvd2luZyB0aGUgbnVtYmVyIGV2ZW50cyBwZXIgc2FtcGxlOgoKYGBge3IgZWNobyA9IEZBTFNFfQojIFBsb3QgZXZlbnRzIHBlciBTaXRlSUQtRGF0ZSBzYW1lIGFzIGJlZm9yZSwKZXZlbnRzLjMgJT4lIHN1bW1hcmlzZShFdmVudHMgPSBuX2Rpc3RpbmN0KEV2ZW50SUQpLCAuYnkgPSBjKFNpdGVJRCwgRGF0ZSkpICU+JQogIGdncGxvdChkYXRhID0gLiwgYWVzKHggPSBFdmVudHMpKSArCiAgZ2VvbV9iYXIoYWVzKHkgPSBhZnRlcl9zdGF0KGNvdW50KSkpICsKICBnZW9tX3RleHQoc3RhdCA9ICdDb3VudCcsIGFlcyhsYWJlbCA9IGFmdGVyX3N0YXQoY291bnQpKSwgdmp1c3QgPSAtMC41KSArCiAgbGFicyh0aXRsZSA9ICJGcmVxdWVuY3kgb2YgZXZlbnRzIHBlciBzYW1wbGUgaW4gb3JpZ2luYWwgZGF0YSBzdHJ1Y3R1cmUiKQojIGdnc2F2ZSgibmZhX2ZyZXEtU2l0ZSZEYXRlLWJ5LXNlaW5lcy5wbmciLCBwbG90ID0gbGFzdF9wbG90KCksIGRldmljZSA9ICdwbmcnLCBwYXRoID0gZmlsZS5wYXRoKGRpci5maWdzKSkKCiMgQW5kIGV2ZW50cyBwZXIgdmlzaXQKZXZlbnRzICU+JSBzdW1tYXJpc2UoRXZlbnRzID0gbl9kaXN0aW5jdChFdmVudElEKSwgLmJ5ID0gVmlzaXRJRCkgJT4lCiAgZ2dwbG90KGRhdGEgPSAuLCBhZXMoeCA9IEV2ZW50cykpICsKICBnZW9tX2JhcihhZXMoeSA9IGFmdGVyX3N0YXQoY291bnQpKSkgKwogIGdlb21fdGV4dChzdGF0ID0gJ0NvdW50JywgYWVzKGxhYmVsID0gYWZ0ZXJfc3RhdChjb3VudCkpLCB2anVzdCA9IC0wLjUpICsKICBsYWJzKHRpdGxlID0gIkZyZXF1ZW5jeSBvZiBldmVudHMgcGVyIHNhbXBsZSBhZnRlciBzb2x2aW5nIFNpdGUtRXZlbnQgcHJvYmxlbSIpCiMgZ2dzYXZlKCJuZmFfZnJlcS12aXNpdHMtYnktc2VpbmVzLnBuZyIsIHBsb3QgPSBsYXN0X3Bsb3QoKSwgZGV2aWNlID0gJ3BuZycsIHBhdGggPSBmaWxlLnBhdGgoZGlyLmZpZ3MpKQoKYGBgCgpXb3cuIFRoZXJlIGFyZSB3YXkgbGVzcyBzaW5nbGUtcHVsbCBzYW1wbGVzISBBbHNvLCB0aGVyZSBhcmUgc29tZSBpbnRlcmVzdGluZyBzYW1wbGVzIHdpdGggcmVwbGljYXRlcyBvZiA5LCAxMCwgYW5kIDEyLiBTb21lIHN0YXRzIG9uIHRoZSBudW1iZXIgb2YgZXZlbnRzIHBlciB2aXNpdDoKCi0gICBtZWFuID0gYHIgc3VtbWFyaXNlKGV2ZW50cywgZXZlbnRzID0gbl9kaXN0aW5jdChFdmVudElEKSwgLmJ5ID0gVmlzaXRJRCkgJT4lIHNlbGVjdCgtVmlzaXRJRCkgJT4lIGFzX3ZlY3RvcigpICU+JSBtZWFuKCkgJT4lIHJvdW5kKDIpYAoKLSAgIG1lZGlhbiA9IGByIHN1bW1hcmlzZShldmVudHMsIGV2ZW50cyA9IG5fZGlzdGluY3QoRXZlbnRJRCksIC5ieSA9IFZpc2l0SUQpICU+JSBzZWxlY3QoLVZpc2l0SUQpICU+JSBhc192ZWN0b3IoKSAlPiUgbWVkaWFuKCkgJT4lIHJvdW5kKDIpYAoKLSAgIHNkID0gYHIgc3VtbWFyaXNlKGV2ZW50cywgZXZlbnRzID0gbl9kaXN0aW5jdChFdmVudElEKSwgLmJ5ID0gVmlzaXRJRCkgJT4lIHNlbGVjdCgtVmlzaXRJRCkgJT4lIGFzX3ZlY3RvcigpICU+JSBzZCgpICU+JSByb3VuZCgyKWAKCiMjIENsZWFuIHVwCgpgYGB7cn0KIyBDbGVhbiB1cCBvdXIgZW52aXJvbm1lbnQgZm9yIHRoZSBuZXh0IHN0ZXA6CnJtKGxpc3QgPSBscygpWyFscygpICVpbiUgYygnZXZlbnRzJywgJ2NhdGNoLjEnLCAnZGF0YScpXSkKCiMgUmVzZXQgZGlyZWN0b3J5CndkID0gaGVyZSgpCmRpcnMgPSB3ZCAlPiUgbGlzdC5maWxlcygpICU+JSBzdHJfc3Vic2V0KHBhdHRlcm4gPSAiXlJFQURNRXxeTElDRU5TRXwubWQkfC5ScHJvaiQiLCBuZWdhdGUgPSBUUlVFKQpmb3IgKGkgaW4gc2VxX2Fsb25nKGRpcnMpKSB7CiAgbmFtZSA9IHN0cl9yZXBsYWNlX2FsbChkaXJzW2ldLCAiXiIsICJkaXIuIikKICBwYXRoID0gc3RyX3JlcGxhY2VfYWxsKGRpcnNbaV0sICJeIiwgc3RyX2Mod2QsICIvIikpCiAgYXNzaWduKG5hbWUsIHBhdGgpCiAgcm0ocGF0aCwgaSkKfQpgYGAKCiMgV3JhbmdsaW5nIGNhdGNoIGRhdGEKClNpbWlsYXIgdG8gb3VyIHdyYW5nbGUgb2Ygc2l0ZSBpbmZvcm1hdGlvbiwgd2Ugd2FudCB0byBob25lIHRoZSBicmVhZHRoIG9mIHRoZSBjYXRjaCBkYXRhIHdlIHVzZS4gQXQgdGhpcyBzdGFnZSwgd2UgZG9uJ3Qgd2FudCB0byBtYWtlIGFueSAqaW50ZXJwcmV0YXRpb25zKiBhYm91dCB0aGUgY2F0Y2guIEluc3RlYWQsIHRoZSBnb2FsIGhlcmUgaXMgdG8gd2VlZCBvdXQgb2JqZWN0aXZlbHkgdW5uZWVkZWQgYW5kIHN1c3BpY2lvdXMgY2FzZXMgYW5kIGVuc3VyZSB0aGUgY29uc2lzdGVuY3kgb2YgdGhlIGRhdGEgKGUuZy4sIGFyZSBhbGwgcG9zc2libGUgc3BlY2llcyBuYW1lZCBhY3R1YWwgbmFtZXM/KS4KCiMjIFNraW1taW5nIGNhdGNoCgpgYGB7cn0Kc2tpbShjYXRjaC4xKQpgYGAKCk9uZSBuaWNlIHRoaW5nIGFib3V0ICoqc2tpbSgpKiogaXMgdGhhdCBpdCBxdWlja2x5IHRlbGxzIHlvdSB3aGVyZSB0aGUgaW5mb3JtYXRpb24gaXMgbWlzc2luZyBhbmQgaG93IG11Y2ggaXMgbWlzc2luZy4gV2Ugc2VlIHRoYXQgdGhlcmUgYXJlIG5pbmUgY2FzZXMgb2YgbWlzc2luZyBkYXRhIGF0IHRoZSBmYW1pbHkgbGV2ZWwuIFNvbWV0aGluZyBlbHNlIHRoYXQganVtcHMgb3V0IHRvIG1lIGlzIHRoYXQgdGhlIG51bWJlciBvZiB1bmlxdWUgc3BlY2llcyBjb21tb24gbmFtZXMgaXMgbm90IHRoZSBzYW1lIGFzIHRoZSBudW1iZXIgb2YgdW5pcXVlIHNwZWNpZXMgc2NpZW50aWZpYyBuYW1lcy4KCkknbGwgYWxzbyBjaGFuZ2UgdGhlICdVbm1lYXN1cmVkJyB2YXJpYWJsZSB0byBhIGNvdW50IGFidW5kYW5jZSwgd2hpY2ggbWF5IG5vdCBiZSB0b3RhbGx5IG5lY2Vzc2FyeSBidXQgd2lsbCBtYWtlIGl0IGVhc2llciBmb3IgbWUgdG8gd29yayB3aXRoIHRoZSBkYXRhLgoKQW5vdGhlciB0aGluZyB0byBwb2ludCBvdXQgaXMgdGhhdCBsZXNzIHRoYW4gaGFsZiBvZiB0aGUgZGF0YSBhcHBlYXJzIHRvIGhhdmUgbGlmZSBzdGFnZSBpbmZvcm1hdGlvbi4gRnVydGhlcm1vcmUsIHRoZSBkYXRhIHdpdGggbGVuZ3RoIGluZm9ybWF0aW9uIGlzIGEgbGl0dGxlIG92ZXIgNTAlLiBXZSBleHBlY3Qgc29tZSBtaXNzaW5nIGxlbmd0aHMgYmVjYXVzZSBtb3N0IHByb2plY3RzIHdpbGwgbWVhc3VyZSBzaXplIGZvciBhIHN1YnNldCBvZiBlYWNoIHNwZWNpZXMgYW5kIGNvdW50IHRoZSByZXN0LiBUeXBpY2FsbHksIEkgZXhwZWN0IDIwLTUwIGluZGl2aWR1YWxzIHNpemVkIGJlZm9yZSBhIHJvdyBpbmRpY2F0ZWQgdGhlIHVubWVhc3VyZWQgY291bnQsIHdoaWNoIHdvdWxkIHJlc3VsdCBpbiBhIHZlcnkgbG93IHBlcmNlbnRhZ2Ugb2YgIm1pc3NpbmciIGxlbmd0aHMgKG1heWJlIDUlIG9yIGxlc3M/KS4gQnV0IG1pc3NpbmcgbGVuZ3RocyBvdmVyIDUwJSBpbmRpY2F0ZSBzb21lIHByb2plY3RzIGp1c3QgZGlkbid0IG1lYXN1cmUgdGhlaXIgY2F0Y2gsIHdoaWNoIGlzIGEgc2hhbWUgYmVjYXVzZSB3ZSBjb3VsZCBoYXZlIGluZmVycmVkIGxpZmUgc3RhZ2UgZnJvbSBsZW5ndGguIEF0IHRoaXMgcG9pbnQsIGl0IGRvZXNuJ3Qgc2VlbSB3b3J0aCB3aGlsZSB0byB0b3NzIG91dCBwb3RlbnRpYWxseSBoYWxmIHRoZSBkYXRhIHRvIGluY2x1ZGUgZWl0aGVyIG9mIHRob3NlIHZhcmlhYmxlcy4gQWx0aG91Z2gsIEkgbWF5IHJldHVybiB0byB0aGlzIGRvd24gdGhlIHJvYWQuCgpGaXJzdCwgbGV0J3MgY2hlY2sgb3V0IGFsbCB0aGUgdGF4YSB3ZSBoYXZlIGluIHRoZSBkYXRhLgoKIyMgVGF4YSBsaXN0IC0gYmVmb3JlIGVkaXRzIHsudGFic2V0IC50YWJzZXQtcGlsbHN9CgojIyMgRmFtaWx5IHsudW5udW1iZXJlZH0KCmBgYHtyIGVjaG8gPSBGQUxTRX0KY2F0Y2guMSAlPiUKICBzZWxlY3QoRmFtX1NjaWVudGlmaWNOYW1lLCBGYW1fQ29tbW9uTmFtZSkgJT4lCiAgcmVuYW1lKGBTY2llbnRpZmljIE5hbWVgID0gRmFtX1NjaWVudGlmaWNOYW1lLCBgQ29tbW9uIE5hbWVgID0gRmFtX0NvbW1vbk5hbWUpICU+JQogIGRpc3RpbmN0KCkgJT4lCiAgYXJyYW5nZShgU2NpZW50aWZpYyBOYW1lYCkgJT4lCiAgZGF0YXRhYmxlKHJvd25hbWVzID0gRkFMU0UsCiAgICAgICAgICAgIGV4dGVuc2lvbnMgPSAnU2Nyb2xsZXInLAogICAgICAgICAgICBvcHRpb25zID0gbGlzdChkZWZlclJlbmRlciA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjcm9sbFkgPSA1MDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjcm9sbGVyID0gVFJVRSkpCmBgYAoKIyMjIFNwZWNpZXMgey51bm51bWJlcmVkfQoKYGBge3IgZWNobyA9IEZBTFNFfQpjYXRjaC4xICU+JQogIHNlbGVjdChTcF9TY2llbnRpZmljTmFtZSwgU3BfQ29tbW9uTmFtZSkgJT4lCiAgcmVuYW1lKGBTY2llbnRpZmljIE5hbWVgID0gU3BfU2NpZW50aWZpY05hbWUsIGBDb21tb24gTmFtZWAgPSBTcF9Db21tb25OYW1lKSAlPiUKICBkaXN0aW5jdCgpICU+JQogIGFycmFuZ2UoYFNjaWVudGlmaWMgTmFtZWApICU+JQogIGRhdGF0YWJsZShyb3duYW1lcyA9IEZBTFNFLAogICAgICAgICAgICBleHRlbnNpb25zID0gJ1Njcm9sbGVyJywKICAgICAgICAgICAgb3B0aW9ucyA9IGxpc3QoZGVmZXJSZW5kZXIgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICBzY3JvbGxZID0gNTAwLAogICAgICAgICAgICAgICAgICAgICAgICAgICBzY3JvbGxlciA9IFRSVUUpKQpgYGAKCiMjIEVkaXRpbmcgdGF4b25vbXkKClRoZSBkaXNjcmVwYW5jeSBiZXR3ZWVuIHNwZWNpZXMgY29tbW9uIGFuZCBzY2llbnRpZmljIG5hbWVzIGhhcyB0byBkbyB3aXRoIHRoZSBjYXNlcyB3aGVyZSBmaXNoIHdlcmUgZ2l2ZW4gdW5pZGVudGlmaWVkIG9yIGp1dmVuaWxlIGxhYmVscy4gTm90aGluZyB0byBmaXggaGVyZSBidXQgd2UnbGwganVzdCBtYWtlIHN1cmUgdG8gdXNlIHNjaWVudGlmaWMgbmFtZXMgaW4gYW5hbHlzZXMuCgpJIGFsc28gbm90aWNlIGEgY291cGxlIG9mIGNhc2VzIHdoZXJlIHNwZWNpZXMgYXJlIGNhbGxlZCBzb21ldGhpbmcgJ29yJyBzb21ldGhpbmcgZWxzZToKCjEuICBBbmlzYXJjaHVzIG1lZGl1cyBvciBMdW1wZW51cyBmYWJyaWNpaQoyLiAgT3NtZXJpZGFlIG9yIENsdXBpZGFlIChtaXNzcGVsbGVkKQoKTG9va2luZyBhdCB0aGUgZmFtaWx5IHRhYmxlLCB3ZSBkb24ndCBzZWUgJ09zbWVyaWRhZSBvciBDbHVwZWlkYWUnLCBzbyBJIHRoaW5rIHRoYXQgcmVwbGFjaW5nIHRoZXNlICdvcicgY2FzZXMgd2l0aCB0aGVpciBhc3NvY2lhdGVkIGZhbWlseSBzY2llbnRpZmljIG5hbWUgc2hvdWxkIHN1ZmZpY2UuIFNwZWFraW5nIG9mIHRoZSBmYW1pbHkgdGFibGUsIEkgc2VlIHRoYXQgdGhvc2UgbWlzc2luZyBkYXRhIHNob3cgdXAgYXMgTkEncy4gV2UnbGwgdGFrZSBhIGxvb2sgYXQgaXQgYW5kIG1ha2UgYSBqdWRnbWVudCBjYWxsIG9uIGhvdyB0byBkZWFsIHdpdGggdGhlbS4KCkxhc3QsIEknbGwgYWxzbyBjcmVhdGUgYSBnZW51cyBjbGFzc2lmaWNhdGlvbiwgd2hpY2ggY291bGQgZW5kIHVwIGJlaW5nIHRoZSBsZXZlbCBhdCB3aGljaCB3ZSBkbyB0aGUgYW5hbHlzZXMsIGRlcGVuZGluZyBvbiB3aGF0IHRoYXQgbG9va3MgbGlrZS4KCkkgZG8gbm90IHNob3cgcmVzdWx0cyBvZiB0aGUgYmVsb3cgY29kZSwgYnV0IEkgcHJvdmlkZWQgYW4gZWRpdGVkIHRheGEgbGlzdCBhbmQgc2VlIGFsc28gY29tbWVudHMgZm9yIHN0ZXBzIHRha2VuLgoKYGBge3IgcmVzdWx0cyA9ICJoaWRlIn0KIyBDaGVjayBvdXQgdGhlIGNhc2VzIHdoZXJlIGZhbWlseSBpcyBtaXNzaW5nCmZpbHRlcihjYXRjaC4xLCBpcy5uYShGYW1fU2NpZW50aWZpY05hbWUpKQojIExvb2sgdXAgdGhlIHZpc2l0IGluZm8gZm9yIHRoZXNlIGNhc2VzCmZpbHRlcihldmVudHMsIEV2ZW50SUQgJWluJSBjYXRjaC4xJEV2ZW50SURbd2hpY2goaXMubmEoY2F0Y2guMSRGYW1fU2NpZW50aWZpY05hbWUpKV0pCiMgQ2hlY2sgb3V0IGV2ZXJ5dGhpbmcgZWxzZSBjYXVnaHQgZHVyaW5nIHRoZXNlIHZpc2l0cwpmaWx0ZXIoY2F0Y2guMSwgRXZlbnRJRCAlaW4lIGNhdGNoLjEkRXZlbnRJRFt3aGljaChpcy5uYShjYXRjaC4xJEZhbV9TY2llbnRpZmljTmFtZSkpXSkKIyBTaW5jZSB0aGVzZSBuaW5lIE5BcyBtYWtlIHVwIGEgc21hbGwgcG9ydGlvbiBvZiB0aGVpciBzYW1wbGVzLAojIFRoZSBzaW1wbGVzdCBzb2x2ZSBoZXJlIGlzIHRvIGp1c3QgcmVtb3ZlIHRoZSBkYXRhLgoKIyBDcmVhdGUgYSBuZXcgb2JqZWN0IGFmdGVyIGVkaXRzIG1hZGUgdG8gdGF4b25vbXkKY2F0Y2guMiA9IGNhdGNoLjEgJT4lCiAgIyBEcm9wIHRoZSBuaW5lIGluc3RhbmNlcyBvZiBOQSBjYXRjaAogIGRyb3BfbmEoRmFtX1NjaWVudGlmaWNOYW1lKSAlPiUKICAjIEZpbmQgdGhlICdvcicgc3BlY2llcyBhbmQgcmVwbGFjZSB3aXRoIEZhbV9TY2llbnRpZmljTmFtZSB2YWx1ZQogIG11dGF0ZShTcF9TY2llbnRpZmljTmFtZSA9IGlmZWxzZShzdHJfZGV0ZWN0KFNwX1NjaWVudGlmaWNOYW1lLCBwYXR0ZXJuID0gIiBvciAiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRmFtX1NjaWVudGlmaWNOYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTcF9TY2llbnRpZmljTmFtZSksCiAgICAgICAgICMgQ3JlYXRlIGdlbnVzIGNsYXNzIGJ5IHRha2luZyBmaXJzdCB3b3JkIG9mIFNwX1NjaWVudGlmaWNOYW1lCiAgICAgICAgIEdlbl9TY2llbnRpZmljTmFtZSA9IHdvcmQoU3BfU2NpZW50aWZpY05hbWUsIDEpKQpgYGAKCiMjIFRheGEgbGlzdCAtIGFmdGVyIGVkaXRzIHsudGFic2V0IC50YWJzZXQtcGlsbHN9CgojIyMgRmFtaWx5IHsudW5udW1iZXJlZH0KCmBgYHtyIGVjaG8gPSBGQUxTRX0KY2F0Y2guMiAlPiUKICBzZWxlY3QoRmFtX1NjaWVudGlmaWNOYW1lLCBGYW1fQ29tbW9uTmFtZSkgJT4lCiAgcmVuYW1lKGBTY2llbnRpZmljIE5hbWVgID0gRmFtX1NjaWVudGlmaWNOYW1lLCBgQ29tbW9uIE5hbWVgID0gRmFtX0NvbW1vbk5hbWUpICU+JQogIGRpc3RpbmN0KCkgJT4lCiAgYXJyYW5nZShgU2NpZW50aWZpYyBOYW1lYCkgJT4lCiAgZGF0YXRhYmxlKHJvd25hbWVzID0gRkFMU0UsCiAgICAgICAgICAgIGV4dGVuc2lvbnMgPSAnU2Nyb2xsZXInLAogICAgICAgICAgICBvcHRpb25zID0gbGlzdChkZWZlclJlbmRlciA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjcm9sbFkgPSA1MDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjcm9sbGVyID0gVFJVRSkpCmBgYAoKIyMjIEdlbnVzIHsudW5udW1iZXJlZH0KCmBgYHtyIGVjaG8gPSBGQUxTRX0KY2F0Y2guMiAlPiUKICBzZWxlY3QoYFNjaWVudGlmaWMgTmFtZWAgPSBHZW5fU2NpZW50aWZpY05hbWUpICU+JQogIGRpc3RpbmN0KCkgJT4lCiAgYXJyYW5nZShgU2NpZW50aWZpYyBOYW1lYCkgJT4lCiAgZGF0YXRhYmxlKHJvd25hbWVzID0gRkFMU0UsCiAgICAgICAgICAgIGV4dGVuc2lvbnMgPSAnU2Nyb2xsZXInLAogICAgICAgICAgICBvcHRpb25zID0gbGlzdChkZWZlclJlbmRlciA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjcm9sbFkgPSA1MDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjcm9sbGVyID0gVFJVRSkpCmBgYAoKIyMjIFNwZWNpZXMgey51bm51bWJlcmVkfQoKYGBge3IgZWNobyA9IEZBTFNFfQpjYXRjaC4yICU+JQogIHNlbGVjdChTcF9TY2llbnRpZmljTmFtZSwgU3BfQ29tbW9uTmFtZSkgJT4lCiAgcmVuYW1lKGBTY2llbnRpZmljIE5hbWVgID0gU3BfU2NpZW50aWZpY05hbWUsIGBDb21tb24gTmFtZWAgPSBTcF9Db21tb25OYW1lKSAlPiUKICBkaXN0aW5jdCgpICU+JQogIGFycmFuZ2UoYFNjaWVudGlmaWMgTmFtZWApICU+JQogIGRhdGF0YWJsZShyb3duYW1lcyA9IEZBTFNFLAogICAgICAgICAgICBleHRlbnNpb25zID0gJ1Njcm9sbGVyJywKICAgICAgICAgICAgb3B0aW9ucyA9IGxpc3QoZGVmZXJSZW5kZXIgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICBzY3JvbGxZID0gNTAwLAogICAgICAgICAgICAgICAgICAgICAgICAgICBzY3JvbGxlciA9IFRSVUUpKQpgYGAKCiMjIFJlcGxhY2luZyAnVW5tZWFzdXJlZCcgd2l0aCAnQ291bnQnCgpUaGUgYmVsb3cgY29kZSBjaGFuZ2VzIHRoZSAnVW5tZWFzdXJlZCcgcGFyYW1ldGVyIGludG8gJ0NvdW50JyBhYnVuZGFuY2U6CgpgYGB7ciByZXN1bHRzID0gImhpZGUifQpjYXRjaC4zID0gbXV0YXRlKGNhdGNoLjIsIENvdW50ID0gaWZlbHNlKFVubWVhc3VyZWQgPT0gMCwgMSwgVW5tZWFzdXJlZCkpICU+JQogIHNlbGVjdCgtVW5tZWFzdXJlZCkgIyByZW1vdmUgdW51c2VkIHBhcmFtZXRlcgpgYGAKCk5leHQsIEknbGwgbWFrZSBncmFwaHMgYW5kIHRhYmxlcyBkZXNjcmliaW5nIHRoZSBhYnVuZGFuY2UgYW5kIG9jY3VycmVuY2Ugb2Ygb3VyIGNhdGNoOgoKIyMgVmlzdWFsaXppbmcgY2F0Y2ggey50YWJzZXQgLnRhYnNldC1waWxsc30KCiMjIyBGYW1pbHkgey51bm51bWJlcmVkfQoKYGBge3IgZWNobyA9IEZBTFNFLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDh9CiMgRmFtaWx5IGFidW5kYW5jZQpmYW0uYWJ1biA9IGNhdGNoLjMgJT4lCiAgc3VtbWFyaXNlKEFidW5kYW5jZSA9IHN1bShDb3VudCksIC5ieSA9IEZhbV9TY2llbnRpZmljTmFtZSkgJT4lCiAgbXV0YXRlKEZhbV9TY2llbnRpZmljTmFtZSA9IGZjdF9yZW9yZGVyKGFzLmZhY3RvcihGYW1fU2NpZW50aWZpY05hbWUpLCBkZXNjKEFidW5kYW5jZSkpKQojIFBsb3QKZ2dwbG90KGRhdCA9IGZhbS5hYnVuLCBhZXMoeCA9IEZhbV9TY2llbnRpZmljTmFtZSwgeSA9IEFidW5kYW5jZSkpICsKICBnZW9tX2NvbCgpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gQWJ1bmRhbmNlKSwgdmp1c3QgPSAtMC41LCBzaXplID0gMikgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSkpICsKICBsYWJzKHRpdGxlID0gJ0ZhbWlseSBhYnVuZGFuY2UnLCB4ID0gJ0ZhbWlseScpCiMgZ2dzYXZlKCJuZmFfZmFtX2FidW5fcmF3LnBuZyIsIHBsb3QgPSBsYXN0X3Bsb3QoKSwgZGV2aWNlID0gJ3BuZycsIHBhdGggPSBmaWxlLnBhdGgoZGlyLmZpZ3MpKQoKIyBGYW1pbHkgZnJlcXVlbmN5IG9mIG9jY3VycmVuY2UKZmFtLmZyZXEub2NjdXIgPSBsZWZ0X2pvaW4oY2F0Y2guMywgc2VsZWN0KGV2ZW50cywgVmlzaXRJRCwgRXZlbnRJRCksIGJ5ID0gIkV2ZW50SUQiKSAlPiUKICBzdW1tYXJpc2UoUHJlc2VuY2UgPSBuX2Rpc3RpbmN0KEZhbV9TY2llbnRpZmljTmFtZSksIC5ieSA9IGMoVmlzaXRJRCwgRmFtX1NjaWVudGlmaWNOYW1lKSkgJT4lCiAgc3VtbWFyaXNlKE9jY3VycmVuY2UgPSBzdW0oUHJlc2VuY2UpLCAuYnkgPSBGYW1fU2NpZW50aWZpY05hbWUpICU+JQogIG11dGF0ZShQZXJjX09jY3VycmVuY2UgPSByb3VuZChPY2N1cnJlbmNlIC8gbl9kaXN0aW5jdChldmVudHMkVmlzaXRJRCkgKiAxMDAsIDIpLAogICAgICAgICBGYW1fU2NpZW50aWZpY05hbWUgPSBmY3RfcmVvcmRlcihhcy5mYWN0b3IoRmFtX1NjaWVudGlmaWNOYW1lKSwgZGVzYyhQZXJjX09jY3VycmVuY2UpKSkKIyBQbG90CmdncGxvdChkYXRhID0gZmFtLmZyZXEub2NjdXIsIGFlcyh4ID0gRmFtX1NjaWVudGlmaWNOYW1lLCB5ID0gUGVyY19PY2N1cnJlbmNlKSkgKwogIGdlb21fY29sKCkgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSByb3VuZChQZXJjX09jY3VycmVuY2UsIDEpKSwgc2l6ZSA9IDIsIHZqdXN0ID0gLTAuNSkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSkpICsKICAgIGxhYnModGl0bGUgPSAnRmFtaWx5IGZyZXF1ZW5jeSBvZiBvY2N1cnJlbmNlJywgeCA9ICdGYW1pbHknLCB5ID0gJ1BlcmNlbnQgb2NjdXJyZW5jZScpCiMgZ2dzYXZlKCJuZmFfZmFtX2ZyZXEtb2NjdXIucG5nIiwgcGxvdCA9IGxhc3RfcGxvdCgpLCBkZXZpY2UgPSAncG5nJywgcGF0aCA9IGZpbGUucGF0aChkaXIuZmlncykpCgojIFRhYmxlIG9mIGZhbWlseSBhYnVuZGFuY2UgYW5kIG9jY3VycmVuY2UKZnVsbF9qb2luKGZhbS5hYnVuLCBmYW0uZnJlcS5vY2N1ciwgYnkgPSAnRmFtX1NjaWVudGlmaWNOYW1lJykgJT4lCiAgc2VsZWN0KGBTY2llbnRpZmljIE5hbWVgID0gRmFtX1NjaWVudGlmaWNOYW1lLCBgUGVyY2VudCBPY2N1cnJlbmNlYCA9IFBlcmNfT2NjdXJyZW5jZSwgQWJ1bmRhbmNlKSAlPiUKICBhcnJhbmdlKGFzLmNoYXJhY3RlcihgU2NpZW50aWZpYyBOYW1lYCkpICU+JQogIGRhdGF0YWJsZShyb3duYW1lcyA9IEZBTFNFLAogICAgICAgICAgICBleHRlbnNpb25zID0gJ1Njcm9sbGVyJywKICAgICAgICAgICAgb3B0aW9ucyA9IGxpc3QoZGVmZXJSZW5kZXIgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICBzY3JvbGxZID0gNTAwLAogICAgICAgICAgICAgICAgICAgICAgICAgICBzY3JvbGxlciA9IFRSVUUpKQpgYGAKCiMjIyBHZW51cyB7LnVubnVtYmVyZWR9CgpgYGB7ciBlY2hvID0gRkFMU0UsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gOH0KIyBHZW51cyBhYnVuZGFuY2UKZ2VuLmFidW4gPSBjYXRjaC4zICU+JQogIHN1bW1hcmlzZShBYnVuZGFuY2UgPSBzdW0oQ291bnQpLCAuYnkgPSBHZW5fU2NpZW50aWZpY05hbWUpICU+JQogIG11dGF0ZShHZW5fU2NpZW50aWZpY05hbWUgPSBmY3RfcmVvcmRlcihhcy5mYWN0b3IoR2VuX1NjaWVudGlmaWNOYW1lKSwgZGVzYyhBYnVuZGFuY2UpKSkKIyBQbG90CmdncGxvdChkYXRhID0gZ2VuLmFidW4sIGFlcyh4ID0gR2VuX1NjaWVudGlmaWNOYW1lLCB5ID0gQWJ1bmRhbmNlKSkgKwogIGdlb21fY29sKCkgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSBBYnVuZGFuY2UpLCB2anVzdCA9IC0wLjUsIHNpemUgPSAyKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkgKwogIGxhYnModGl0bGUgPSAnR2VudXMgYWJ1bmRhbmNlJywgeCA9ICdHZW51cycpCiMgZ2dzYXZlKCJuZmFfZ2VuX2FidW5fcmF3LnBuZyIsIHBsb3QgPSBsYXN0X3Bsb3QoKSwgZGV2aWNlID0gJ3BuZycsIHBhdGggPSBmaWxlLnBhdGgoZGlyLmZpZ3MpKQoKIyBHZW51cyBmcmVxdWVuY3kgb2Ygb2NjdXJyZW5jZQpnZW4uZnJlcS5vY2N1ciA9IGxlZnRfam9pbihjYXRjaC4zLCBzZWxlY3QoZXZlbnRzLCBWaXNpdElELCBFdmVudElEKSwgYnkgPSAiRXZlbnRJRCIpICU+JQogIHN1bW1hcmlzZShQcmVzZW5jZSA9IG5fZGlzdGluY3QoR2VuX1NjaWVudGlmaWNOYW1lKSwgLmJ5ID0gYyhWaXNpdElELCBHZW5fU2NpZW50aWZpY05hbWUpKSAlPiUKICBzdW1tYXJpc2UoT2NjdXJyZW5jZSA9IHN1bShQcmVzZW5jZSksIC5ieSA9IEdlbl9TY2llbnRpZmljTmFtZSkgJT4lCiAgbXV0YXRlKFBlcmNfT2NjdXJyZW5jZSA9IHJvdW5kKE9jY3VycmVuY2UgLyBuX2Rpc3RpbmN0KGV2ZW50cyRWaXNpdElEKSAqIDEwMCwgMiksCiAgICAgICAgIEdlbl9TY2llbnRpZmljTmFtZSA9IGZjdF9yZW9yZGVyKGFzLmZhY3RvcihHZW5fU2NpZW50aWZpY05hbWUpLCBkZXNjKFBlcmNfT2NjdXJyZW5jZSkpKQojIFBsb3QgCmdncGxvdChkYXRhID0gZ2VuLmZyZXEub2NjdXIsIGFlcyh4ID0gR2VuX1NjaWVudGlmaWNOYW1lLCB5ID0gUGVyY19PY2N1cnJlbmNlKSkgKwogIGdlb21fY29sKCkgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSByb3VuZChQZXJjX09jY3VycmVuY2UsIDEpKSwgc2l6ZSA9IDIsIHZqdXN0ID0gLTAuNSkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSkpICsKICAgIGxhYnModGl0bGUgPSAnR2VudXMgZnJlcXVlbmN5IG9mIG9jY3VycmVuY2UnLCB4ID0gJ0dlbnVzJywgeSA9ICdQZXJjZW50IG9jY3VycmVuY2UnKQojIGdnc2F2ZSgibmZhX2dlbl9mcmVxLW9jY3VyLnBuZyIsIHBsb3QgPSBsYXN0X3Bsb3QoKSwgZGV2aWNlID0gJ3BuZycsIHBhdGggPSBmaWxlLnBhdGgoZGlyLmZpZ3MpKQoKIyBUYWJsZSBvZiBnZW51cyBhYnVuZGFuY2UgYW5kIG9jY3VycmVuY2UKZnVsbF9qb2luKGdlbi5hYnVuLCBnZW4uZnJlcS5vY2N1ciwgYnkgPSAnR2VuX1NjaWVudGlmaWNOYW1lJykgJT4lCiAgc2VsZWN0KGBTY2llbnRpZmljIE5hbWVgID0gR2VuX1NjaWVudGlmaWNOYW1lLCBgUGVyY2VudCBPY2N1cnJlbmNlYCA9IFBlcmNfT2NjdXJyZW5jZSwgQWJ1bmRhbmNlKSAlPiUKICBhcnJhbmdlKGFzLmNoYXJhY3RlcihgU2NpZW50aWZpYyBOYW1lYCkpICU+JQogIGRhdGF0YWJsZShyb3duYW1lcyA9IEZBTFNFLAogICAgICAgICAgICBleHRlbnNpb25zID0gJ1Njcm9sbGVyJywKICAgICAgICAgICAgb3B0aW9ucyA9IGxpc3QoZGVmZXJSZW5kZXIgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICBzY3JvbGxZID0gNTAwLAogICAgICAgICAgICAgICAgICAgICAgICAgICBzY3JvbGxlciA9IFRSVUUpKQpgYGAKCiMjIyBTcGVjaWVzIHsudW5udW1iZXJlZH0KCmBgYHtyIGVjaG8gPSBGQUxTRSwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSA4fQojIFNwZWNpZXMgYWJ1bmRhbmNlCnNwLmFidW4gPSBjYXRjaC4zICU+JQogIHN1bW1hcmlzZShBYnVuZGFuY2UgPSBzdW0oQ291bnQpLCAuYnkgPSBTcF9TY2llbnRpZmljTmFtZSkgJT4lCiAgbXV0YXRlKFNwX1NjaWVudGlmaWNOYW1lID0gZmN0X3Jlb3JkZXIoYXMuZmFjdG9yKFNwX1NjaWVudGlmaWNOYW1lKSwgZGVzYyhBYnVuZGFuY2UpKSkKIyBQbG90CmdncGxvdChkYXRhID0gc3AuYWJ1biwgYWVzKHggPSBTcF9TY2llbnRpZmljTmFtZSwgeSA9IEFidW5kYW5jZSkpICsKICBnZW9tX2NvbCgpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gQWJ1bmRhbmNlKSwgdmp1c3QgPSAtMC41LCBzaXplID0gMikgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSkpICsKICBsYWJzKHRpdGxlID0gJ1NwZWNpZXMgYWJ1bmRhbmNlJywgeCA9ICdTcGVjaWVzJykKIyBnZ3NhdmUoIm5mYV9zcF9hYnVuX3Jhdy5wbmciLCBwbG90ID0gbGFzdF9wbG90KCksIGRldmljZSA9ICdwbmcnLCBwYXRoID0gZmlsZS5wYXRoKGRpci5maWdzKSkKCiMgU3BlY2llcyBmcmVxdWVuY3kgb2Ygb2NjdXJyZW5jZQpzcC5mcmVxLm9jY3VyID0gbGVmdF9qb2luKGNhdGNoLjMsIHNlbGVjdChldmVudHMsIFZpc2l0SUQsIEV2ZW50SUQpLCBieSA9ICJFdmVudElEIikgJT4lCiAgc3VtbWFyaXNlKFByZXNlbmNlID0gbl9kaXN0aW5jdChTcF9TY2llbnRpZmljTmFtZSksIC5ieSA9IGMoVmlzaXRJRCwgU3BfU2NpZW50aWZpY05hbWUpKSAlPiUKICBzdW1tYXJpc2UoT2NjdXJyZW5jZSA9IHN1bShQcmVzZW5jZSksIC5ieSA9IFNwX1NjaWVudGlmaWNOYW1lKSAlPiUKICBtdXRhdGUoUGVyY19PY2N1cnJlbmNlID0gcm91bmQoT2NjdXJyZW5jZSAvIG5fZGlzdGluY3QoZXZlbnRzJFZpc2l0SUQpICogMTAwLCAyKSwKICAgICAgICAgU3BfU2NpZW50aWZpY05hbWUgPSBmY3RfcmVvcmRlcihhcy5mYWN0b3IoU3BfU2NpZW50aWZpY05hbWUpLCBkZXNjKFBlcmNfT2NjdXJyZW5jZSkpKQojIFBsb3QKZ2dwbG90KGRhdGEgPSBzcC5mcmVxLm9jY3VyLCBhZXMoeCA9IFNwX1NjaWVudGlmaWNOYW1lLCB5ID0gUGVyY19PY2N1cnJlbmNlKSkgKwogIGdlb21fY29sKCkgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSByb3VuZChQZXJjX09jY3VycmVuY2UsIDEpKSwgc2l6ZSA9IDIsIHZqdXN0ID0gLTAuNSkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSkpICsKICAgIGxhYnModGl0bGUgPSAnU3BlY2llcyBmcmVxdWVuY3kgb2Ygb2NjdXJyZW5jZScsIHggPSAnU3BlY2llcycsIHkgPSAnUGVyY2VudCBvY2N1cnJlbmNlJykKIyBnZ3NhdmUoIm5mYV9zcF9mcmVxLW9jY3VyLnBuZyIsIHBsb3QgPSBsYXN0X3Bsb3QoKSwgZGV2aWNlID0gJ3BuZycsIHBhdGggPSBmaWxlLnBhdGgoZGlyLmZpZ3MpKQoKIyBUYWJsZSBvZiBzcGVjaWVzIGFidW5kYW5jZSBhbmQgb2NjdXJyZW5jZQpmdWxsX2pvaW4oc3AuYWJ1biwgc3AuZnJlcS5vY2N1ciwgYnkgPSAnU3BfU2NpZW50aWZpY05hbWUnKSAlPiUKICBzZWxlY3QoYFNjaWVudGlmaWMgTmFtZWAgPSBTcF9TY2llbnRpZmljTmFtZSwgYFBlcmNlbnQgT2NjdXJyZW5jZWAgPSBQZXJjX09jY3VycmVuY2UsIEFidW5kYW5jZSkgJT4lCiAgYXJyYW5nZShhcy5jaGFyYWN0ZXIoYFNjaWVudGlmaWMgTmFtZWApKSAlPiUKICBkYXRhdGFibGUocm93bmFtZXMgPSBGQUxTRSwKICAgICAgICAgICAgZXh0ZW5zaW9ucyA9ICdTY3JvbGxlcicsCiAgICAgICAgICAgIG9wdGlvbnMgPSBsaXN0KGRlZmVyUmVuZGVyID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgc2Nyb2xsWSA9IDUwMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgc2Nyb2xsZXIgPSBUUlVFKSkKYGBgCgojIyBEZWZpbmluZyByYXJlIHRheGEKClJlbW92aW5nIHJhcmUgc3BlY2llcyBpcyBjb21tb24gd2hlbiBkZWFsaW5nIHdpdGggY29tbXVuaXR5IGRhdGEgYmVjYXVzZSBvZiB0aGUgYWZmZWN0IHRoYXQgdGhlIG1vc3QgcmFyZSBzcGVjaWVzIChvdXRsaWVycykgd291bGQgaGF2ZSBpbiB0eXBpY2FsIGFuYWx5c2VzIHRvIGJlIGNvbmR1Y3RlZCAoZS5nLiwgYW5hbHlzaXMgb2YgdmFyaWFuY2UpLiBEZXBlbmRpbmcgb24gb3VyIHJlc2VhcmNoIHF1ZXN0aW9ucywgd2UgbWF5IG5vdCBjb25zaWRlciByYXJlIGNhc2VzIGZ1bmN0aW9uYWwgbWVtYmVycyBvZiB0aGUgY29tbXVuaXR5LSBpbiB0aGVzZSBjYXNlcyBpdCBtYXkgbWFrZSBtb3JlIHNlbnNlIHRvIHZpZXcgdGhlIGNvbW11bml0eSBhcyB0aG9zZSB0YXhhIG1vc3Qgd2VsbCByZXByZXNlbnRlZC4gQnV0IGlmIEkgd2VyZSBtb3JlIGludGVyZXN0ZWQgaW4gcmljaG5lc3MsIHRoZW4gSSB3b3VsZCB3YW50IHRvIGNvbnNlcnZhdGl2ZWx5IHJldGFpbiByYXJlIHRheGEuCgpJIHRoaW5rIHdlIGNhbiBjb252aW5jZSBvdXJzZWx2ZXMgdGhhdCByZW1vdmluZyBzaW5nbGUgY291bnQgYW5kIGV2ZW4gc2luZ2xlIG9jY3VyZW5jZSBzcGVjaWVzIHdvdWxkIGJlIHJlYXNvbmFibGUsIGJlY2F1c2Ugd2UgZG8gbm90IGhhdmUgYW55IGluZm9ybWF0aW9uIHRvIGxldCB1cyBrbm93IHdoZXRoZXIgdGhvc2UgY2FzZXMgcmVwcmVzZW50IGFuIGFjdHVhbCBjb21tdW5pdHkgbWVtYmVyIG9yIHdoZXRoZXIgdGhleSByZXByZXNlbnQgc29tZSBzb3J0IG9mIHNhbXBsaW5nIGVycm9yIChlLmcuLCBtaXN0YWtlbmx5IGxhYmVsbGluZyBhbGwgdGhlIHNhbG1vbmlkcyBpbiBhIHBhcnRpY3VsYXIgc2FtcGxlIEF0bGFudGljIFNhbG1vbiByYXRoZXIgdGhhbiBhIFBhY2lmaWMpLiBPdXIgcmVzZWFyY2ggcXVlc3Rpb25zIGJyb2FkbHkgZGVhbCB3aXRoIHNwYXRpYWwgYW5kIHRlbXBvcmFsIGRpc3RyaWJ1dGlvbnMgb2YgY29tbXVuaXR5IHN0cnVjdHVyZSwgc28gSSB0aGluayBhICdtaWRkbGUgb2YgdGhlIHJvYWQnIGFwcHJvYWNoIGlzIGhvdyB3ZSdsbCB3YW50IHRvIHByb2NlZWQgKGF0IGxlYXN0IGZvciBub3cpLgoKV2UnbGwgd2FudCB0byBiZSBvYmplY3RpdmUgaW4gaG93IHdlIGRlZmluZSByYXJlIHRheGEuIFRvIG1lLCBpdCBtYWtlcyBzZW5zZSB0byBjb25zaWRlciBvY2N1cnJlbmNlIHJhdGhlciB0aGFuIGFidW5kYW5jZS4gT3VyIGRlY2lzaW9uIHNob3VsZCBiYWxhbmNlIHRoZSBpbmZvcm1hdGlvbiBsb3N0IHZzIHJldGFpbmVkLCBhbmQgY29uc2lkZXIgaG93IGl0IGFmZmVjdHMgZWFjaCBsZXZlbCBvZiB0YXhvbm9teS4gSG93ZXZlciwgSSBkbyBub3Qga25vdyB3aGF0IHRheGEgbGV2ZWwgbWFrZXMgbW9zdCBzZW5zZSBmb3IgZnV0dXJlIGFuYWx5c2VzIGF0IHRoaXMgcG9pbnQgc28gd2UnbGwgd2FudCB0byBrZWVwIG91ciBvcHRpb25zIG9wZW4uIEZvciBub3csIGxldCdzIGNvbnNpZGVyIGl0ICJyYXJlIiB3aGVuIHRheGEgYXBwZWFyIGluIGxlc3MgdGhhbiA1IHRvdGFsIHZpc2l0cyAobGVzcyB0aGFuIDAuMjUlIHBlcmNlbnQgb2NjdXJyZW5jZSkuIFdpdGggdGhhdCwgcmVtb3ZpbmcgcmFyZSB0YXhhIGNhdXNlczoKCi0gMTcuNiUgcmVkdWN0aW9uIGluIHJpY2huZXNzIGF0IHRoZSBGYW1pbHkgbGV2ZWwgKDYgb2YgMzQpCgotIDI1LjYlIHJlZHVjdGlvbiBpbiByaWNobmVzcyBhdCB0aGUgR2VudXMgbGV2ZWwgKDMwIG9mIDExNykKCi0gMzEuNCUgcmVkdWN0aW9uIGluIHJpY2huZXNzIGF0IHRoZSBTcGVjaWVzIExldmVsICg1OSBvZiAxODgpCgpOb3RlIHRoYXQgdGhpcyBmaXhlZCBjdXQtb2ZmIGFmZmVjdHMgc3BlY2llcyBkaXZlcnNpdHkgbW9yZSBzbyB0aGFuIGZhbWlseSBkaXZlcnNpdHkuIFNvbWUgb2YgdGhpcyBjYW4gYmUgZXhwbGFpbmVkIGJ5IHRoZSBmYWN0IHRoYXQgdGhlIHNwZWNpZXMgbGlzdCBjb250YWlucyBtYW55IGlkZW50aWZpY2F0aW9ucyBhdCB0aGUgZ2VudXMgYW5kIGZhbWlseSBsZXZlbCwgd2hpY2ggbWFrZXMgc2Vuc2Ugc2luY2UgaWRlbnRpZmljYXRpb24gb2Z0ZW4gaW5jbHVkZXMgY2FzZXMgd2hlcmUgcmVzZWFyY2hlcnMgYWdncmVnYXRlICdpZmZ5JyBJRCdzIGludG8gYSBoaWdoZXIgY2xhc3MuIEkgZXhwZWN0IHRoaXMgaXNzdWUgdG8gYmUgZXNwZWNpYWxseSBhcHBhcmVudCBpbiBhIGNvbGxhYm9yYXRpdmUgZGF0YXNldCBsaWtlIHRoZSBORkEuCgojIyBMaXN0cyBvZiByYXJlIHRheGEgey50YWJzZXQgLnRhYnNldC1waWxsc30KCiMjIyBGYW1pbHkgey51bm51bWJlcmVkfQoKYGBge3IgZWNobyA9IEZBTFNFfQojIFN1YnNldCByYXJlIGZhbWlsaWVzCnJhcmUuZmFtID0gZnVsbF9qb2luKGZhbS5hYnVuLCBmYW0uZnJlcS5vY2N1ciwgYnkgPSAnRmFtX1NjaWVudGlmaWNOYW1lJykgJT4lCiAgZmlsdGVyKFBlcmNfT2NjdXJyZW5jZSA8IDAuMjUpICU+JQogIHNlbGVjdChGYW1fU2NpZW50aWZpY05hbWUsIFBlcmNfT2NjdXJyZW5jZSwgQWJ1bmRhbmNlKQojIFRhYmxlIG9mIHJhcmUgZmFtaWx5IGFidW5kYW5jZSBhbmQgb2NjdXJyZW5jZQpyZW5hbWUocmFyZS5mYW0sIGBTY2llbnRpZmljIE5hbWVgID0gRmFtX1NjaWVudGlmaWNOYW1lLCBgUGVyY2VudCBPY2N1cnJlbmNlYCA9IFBlcmNfT2NjdXJyZW5jZSkgJT4lCiAgYXJyYW5nZShhcy5jaGFyYWN0ZXIoYFNjaWVudGlmaWMgTmFtZWApKSAlPiUKICBkYXRhdGFibGUocm93bmFtZXMgPSBGQUxTRSwKICAgICAgICAgICAgZXh0ZW5zaW9ucyA9ICdTY3JvbGxlcicsCiAgICAgICAgICAgIG9wdGlvbnMgPSBsaXN0KGRlZmVyUmVuZGVyID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgc2Nyb2xsWSA9IDI1MCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgc2Nyb2xsZXIgPSBUUlVFKSkKYGBgCgojIyMgR2VudXMgey51bm51bWJlcmVkfQoKYGBge3IgZWNobyA9IEZBTFNFfQojIFN1YnNldCByYXJlIGdlbnVzCnJhcmUuZ2VuID0gZnVsbF9qb2luKGdlbi5hYnVuLCBnZW4uZnJlcS5vY2N1ciwgYnkgPSAnR2VuX1NjaWVudGlmaWNOYW1lJykgJT4lCiAgZmlsdGVyKFBlcmNfT2NjdXJyZW5jZSA8IDAuMjUpICU+JQogIHNlbGVjdChHZW5fU2NpZW50aWZpY05hbWUsIFBlcmNfT2NjdXJyZW5jZSwgQWJ1bmRhbmNlKQojIFRhYmxlIG9mIHJhcmUgZ2VudXMgYWJ1bmRhbmNlIGFuZCBvY2N1cnJlbmNlCnJlbmFtZShyYXJlLmdlbiwgYFNjaWVudGlmaWMgTmFtZWAgPSBHZW5fU2NpZW50aWZpY05hbWUsIGBQZXJjZW50IE9jY3VycmVuY2VgID0gUGVyY19PY2N1cnJlbmNlKSAlPiUKICBhcnJhbmdlKGFzLmNoYXJhY3RlcihgU2NpZW50aWZpYyBOYW1lYCkpICU+JQogIGRhdGF0YWJsZShyb3duYW1lcyA9IEZBTFNFLAogICAgICAgICAgICBleHRlbnNpb25zID0gJ1Njcm9sbGVyJywKICAgICAgICAgICAgb3B0aW9ucyA9IGxpc3QoZGVmZXJSZW5kZXIgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICBzY3JvbGxZID0gMjUwLAogICAgICAgICAgICAgICAgICAgICAgICAgICBzY3JvbGxlciA9IFRSVUUpKQpgYGAKCiMjIyBTcGVjaWVzIHsudW5udW1iZXJlZH0KCmBgYHtyIGVjaG8gPSBGQUxTRX0KIyBTdWJzZXQgcmFyZSBzcGVjaWVzCnJhcmUuc3AgPSBmdWxsX2pvaW4oc3AuYWJ1biwgc3AuZnJlcS5vY2N1ciwgYnkgPSAnU3BfU2NpZW50aWZpY05hbWUnKSAlPiUKICBmaWx0ZXIoUGVyY19PY2N1cnJlbmNlIDwgMC4yNSkgJT4lCiAgc2VsZWN0KFNwX1NjaWVudGlmaWNOYW1lLCBQZXJjX09jY3VycmVuY2UsIEFidW5kYW5jZSkKIyBUYWJsZSBvZiByYXJlIGdlbnVzIGFidW5kYW5jZSBhbmQgb2NjdXJyZW5jZQpyZW5hbWUocmFyZS5zcCwgYFNjaWVudGlmaWMgTmFtZWAgPSBTcF9TY2llbnRpZmljTmFtZSwgYFBlcmNlbnQgT2NjdXJyZW5jZWAgPSBQZXJjX09jY3VycmVuY2UpICU+JQogIGFycmFuZ2UoYXMuY2hhcmFjdGVyKGBTY2llbnRpZmljIE5hbWVgKSkgJT4lCiAgZGF0YXRhYmxlKHJvd25hbWVzID0gRkFMU0UsCiAgICAgICAgICAgIGV4dGVuc2lvbnMgPSAnU2Nyb2xsZXInLAogICAgICAgICAgICBvcHRpb25zID0gbGlzdChkZWZlclJlbmRlciA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjcm9sbFkgPSAyNTAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjcm9sbGVyID0gVFJVRSkpCmBgYAoKQ29vbC0gbm93IGxldCdzIHJlLXZpc2l0IG91ciBhYnVuZGFuY2UgYW5kIG9jY3VycmVuY2UgZ3JhcGhzIHRvIHNlZSBob3cgdGhleSBsb29rOgoKIyMgQ2F0Y2ggYWZ0ZXIgcmVtb3ZpbmcgcmFyZSB0YXhhIHsudGFic2V0IC50YWJzZXQtcGlsbHN9CgojIyMgRmFtaWx5IHsudW5udW1iZXJlZH0KCmBgYHtyIGVjaG8gPSBGQUxTRSwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSA4fQojIFBsb3QgYWJ1bmRhbmNlCmFudGlfam9pbihmYW0uYWJ1biwgcmFyZS5mYW0sIGJ5ID0gIkZhbV9TY2llbnRpZmljTmFtZSIpICU+JQogIGdncGxvdChhZXMoeCA9IEZhbV9TY2llbnRpZmljTmFtZSwgeSA9IEFidW5kYW5jZSkpICsKICBnZW9tX2NvbCgpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gQWJ1bmRhbmNlKSwgdmp1c3QgPSAtMC41LCBzaXplID0gMikgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSkpICsKICBsYWJzKHRpdGxlID0gJ0ZhbWlseSBhYnVuZGFuY2UgZXhjbHVkaW5nIHJhcmUgdGF4YScsIHggPSAnRmFtaWx5JykKIyBnZ3NhdmUoIm5mYV9mYW1fYWJ1bl9yYXdfZXgtcmFyZS5wbmciLCBwbG90ID0gbGFzdF9wbG90KCksIGRldmljZSA9ICdwbmcnLCBwYXRoID0gZmlsZS5wYXRoKGRpci5maWdzKSkKCiMgUGxvdCBmcmVxdWVuY3kgb2Ygb2NjdXJyZW5jZQphbnRpX2pvaW4oZmFtLmZyZXEub2NjdXIsIHJhcmUuZmFtLCBieSA9ICJGYW1fU2NpZW50aWZpY05hbWUiKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBGYW1fU2NpZW50aWZpY05hbWUsIHkgPSBQZXJjX09jY3VycmVuY2UpKSArCiAgZ2VvbV9jb2woKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHJvdW5kKFBlcmNfT2NjdXJyZW5jZSwgMSkpLCBzaXplID0gMiwgdmp1c3QgPSAtMC41KSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkgKwogICAgbGFicyh0aXRsZSA9ICdGYW1pbHkgZnJlcXVlbmN5IG9mIG9jY3VycmVuY2UgZXhjbHVkaW5nIHJhcmUgdGF4YScsIHggPSAnRmFtaWx5JywgeSA9ICdQZXJjZW50IG9jY3VycmVuY2UnKQojIGdnc2F2ZSgibmZhX2ZhbV9mcmVxLW9jY3VyX2V4LXJhcmUucG5nIiwgcGxvdCA9IGxhc3RfcGxvdCgpLCBkZXZpY2UgPSAncG5nJywgcGF0aCA9IGZpbGUucGF0aChkaXIuZmlncykpCmBgYAoKIyMjIEdlbnVzIHsudW5udW1iZXJlZH0KCmBgYHtyIGVjaG8gPSBGQUxTRSwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSA4fQojIFBsb3QgYWJ1bmRhbmNlCmFudGlfam9pbihnZW4uYWJ1biwgcmFyZS5nZW4pICU+JQogIGdncGxvdChhZXMoeCA9IEdlbl9TY2llbnRpZmljTmFtZSwgeSA9IEFidW5kYW5jZSkpICsKICBnZW9tX2NvbCgpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gQWJ1bmRhbmNlKSwgdmp1c3QgPSAtMC41LCBzaXplID0gMikgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSkpICsKICBsYWJzKHRpdGxlID0gJ0dlbnVzIGFidW5kYW5jZSBleGNsdWRpbmcgcmFyZSB0YXhhJywgeCA9ICdHZW51cycpCiMgZ2dzYXZlKCJuZmFfZ2VuX2FidW5fcmF3X2V4LXJhcmUucG5nIiwgcGxvdCA9IGxhc3RfcGxvdCgpLCBkZXZpY2UgPSAncG5nJywgcGF0aCA9IGZpbGUucGF0aChkaXIuZmlncykpCgojIFBsb3QgZnJlcXVlbmN5IG9mIG9jY3VycmVuY2UKYW50aV9qb2luKGdlbi5mcmVxLm9jY3VyLCByYXJlLmdlbikgJT4lCiAgZ2dwbG90KGFlcyh4ID0gR2VuX1NjaWVudGlmaWNOYW1lLCB5ID0gUGVyY19PY2N1cnJlbmNlKSkgKwogIGdlb21fY29sKCkgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSByb3VuZChQZXJjX09jY3VycmVuY2UsIDEpKSwgc2l6ZSA9IDIsIHZqdXN0ID0gLTAuNSkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSkpICsKICAgIGxhYnModGl0bGUgPSAnR2VudXMgZnJlcXVlbmN5IG9mIG9jY3VycmVuY2UgZXhjbHVkaW5nIHJhcmUgdGF4YScsIHggPSAnR2VudXMnLCB5ID0gJ1BlcmNlbnQgb2NjdXJyZW5jZScpCiMgZ2dzYXZlKCJuZmFfZ2VuX2ZyZXEtb2NjdXJfZXgtcmFyZS5wbmciLCBwbG90ID0gbGFzdF9wbG90KCksIGRldmljZSA9ICdwbmcnLCBwYXRoID0gZmlsZS5wYXRoKGRpci5maWdzKSkKYGBgCgojIyMgU3BlY2llcyB7LnVubnVtYmVyZWR9CgpgYGB7ciBlY2hvID0gRkFMU0UsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gOH0KIyBQbG90IGFidW5kYW5jZQphbnRpX2pvaW4oc3AuYWJ1biwgcmFyZS5zcCkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gU3BfU2NpZW50aWZpY05hbWUsIHkgPSBBYnVuZGFuY2UpKSArCiAgZ2VvbV9jb2woKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IEFidW5kYW5jZSksIHZqdXN0ID0gLTAuNSwgc2l6ZSA9IDIpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKSArCiAgbGFicyh0aXRsZSA9ICdTcGVjaWVzIGFidW5kYW5jZSBleGNsdWRpbmcgcmFyZSB0YXhhJywgeCA9ICdTcGVjaWVzJykKIyBnZ3NhdmUoIm5mYV9zcF9hYnVuX3Jhd19leC1yYXJlLnBuZyIsIHBsb3QgPSBsYXN0X3Bsb3QoKSwgZGV2aWNlID0gJ3BuZycsIHBhdGggPSBmaWxlLnBhdGgoZGlyLmZpZ3MpKQoKIyBQbG90IGZyZXF1ZW5jeSBvZiBvY2N1cnJlbmNlCmFudGlfam9pbihzcC5mcmVxLm9jY3VyLCByYXJlLnNwKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBTcF9TY2llbnRpZmljTmFtZSwgeSA9IFBlcmNfT2NjdXJyZW5jZSkpICsKICBnZW9tX2NvbCgpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcm91bmQoUGVyY19PY2N1cnJlbmNlLCAxKSksIHNpemUgPSAyLCB2anVzdCA9IC0wLjUpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKSArCiAgICBsYWJzKHRpdGxlID0gJ1NwZWNpZXMgZnJlcXVlbmN5IG9mIG9jY3VycmVuY2UgZXhjbHVkaW5nIHJhcmUgdGF4YScsIHggPSAnU3BlY2llcycsIHkgPSAnUGVyY2VudCBvY2N1cnJlbmNlJykKIyBnZ3NhdmUoIm5mYV9zcF9mcmVxLW9jY3VyX2V4LXJhcmUucG5nIiwgcGxvdCA9IGxhc3RfcGxvdCgpLCBkZXZpY2UgPSAncG5nJywgcGF0aCA9IGZpbGUucGF0aChkaXIuZmlncykpCmBgYAoKIyMgQXZlcmFnaW5nIGFidW5kYW5jZSBwZXIgdmlzaXQKCkJlY2F1c2Ugb3VyIHNhbXBsZXMgKHZpc2l0cykgY29udGFpbiBhIHZhcmlhYmxlIG51bWJlciBvZiByZXBsaWNhdGVzIChldmVudHMpLCB3ZSBzaG91bGQgdXNlIGFuIGF2ZXJhZ2luZyBtZXRob2QgdG8gY2FsY3VsYXRlIGFidW5kYW5jZS4gSGVyZSwgd2UnbGwgdXNlIGEgc2ltcGxlIG1lYW4gKGkuZS4sIHRvdGFsIGNvdW50IC8gbnVtYmVyIG9mIHJlcGxpY2F0ZXMpLiBUaGlzIGlzIG91ciBmaXJzdCBpbnN0YW5jZSBvZiBkYXRhIHN0YW5kYXJkaXphdGlvbiwgd2hpY2ggbWF5IGJlIGZ1cnRoZXIgc3RhbmRhcmRpemVkIG9yIHRyYW5zZm9ybWVkIGRlcGVuZGluZyBvbiBuZXh0IHN0ZXBzIGFuYWx5c2VzLiBBbHNvLCBhdCBzb21lIHBvaW50IGluIHRoZSBmdXR1cmUgaXQnZCBiZSBuZWF0IHRvIHVzZSB0aGUgc2l6ZSB2YXJpYWJsZSB0byBiaW4gb3VyIHRheGEgYnkgbGVuZ3RoLiBCdXQgZm9yIG5vdywgd2UnbGwgbW92ZSBmb3J3YXJkIHNpbXBseSB1c2luZyB0aGUgYWJ1bmRhbmNlIGRhdGEgKG1pbnVzIHJhcmUgdGF4YSkuCgpgYGB7cn0KIyMgRmFtaWx5IGFidW5kYW5jZSBwZXIgdmlzaXQKIyBTYXZlIGFuIG9iamVjdCBvZiBhbGwgdGhlIHJhcmUgZmFtaWxpZXMKcmFyZS5mYW1pbGllcyA9IHVuaXF1ZShyYXJlLmZhbSRGYW1fU2NpZW50aWZpY05hbWUpICU+JSBhcy5jaGFyYWN0ZXIoKQojIENyZWF0ZSBhIHRpYmJsZSBvZiBmYW1pbHkgYWJ1bmRhbmNlIGV4Y2x1ZGluZyByYXJlIGZhbWlsaWVzCmNhdGNoLmZhbSA9IGxlZnRfam9pbihzZWxlY3QoZXZlbnRzLCBWaXNpdElELCBFdmVudElEKSwgY2F0Y2guMywgYnkgPSAiRXZlbnRJRCIpICU+JQogIHNlbGVjdChWaXNpdElELAogICAgICAgICBFdmVudElELAogICAgICAgICBGYW1fU2NpZW50aWZpY05hbWUsCiAgICAgICAgIENvdW50KSAlPiUKICBmaWx0ZXIoIUZhbV9TY2llbnRpZmljTmFtZSAlaW4lIHJhcmUuZmFtaWxpZXMpICU+JSAjIHJlbW92ZSByYXJlIHRheGEKICBzdW1tYXJpc2UoQWJ1bmRhbmNlID0gc3VtKENvdW50KSAvIG5fZGlzdGluY3QoRXZlbnRJRCksIC5ieSA9IGMoVmlzaXRJRCwgRmFtX1NjaWVudGlmaWNOYW1lKSkKICAKIyMgR2VudXMgYWJ1bmRhbmNlIHBlciB2aXNpdAojIFNhdmUgYW4gb2JqZWN0IG9mIGFsbCB0aGUgcmFyZSBnZW51cwpyYXJlLmdlbnVzID0gdW5pcXVlKHJhcmUuZ2VuJEdlbl9TY2llbnRpZmljTmFtZSkgJT4lIGFzLmNoYXJhY3RlcigpCiMgQ3JlYXRlIGEgdGliYmxlIG9mIGdlbnVzIGFidW5kYW5jZSBleGNsdWRpbmcgcmFyZSBnZW51cwpjYXRjaC5nZW4gPSBsZWZ0X2pvaW4oc2VsZWN0KGV2ZW50cywgVmlzaXRJRCwgRXZlbnRJRCksIGNhdGNoLjMsIGJ5ID0gIkV2ZW50SUQiKSAlPiUKICBzZWxlY3QoVmlzaXRJRCwKICAgICAgICAgRXZlbnRJRCwKICAgICAgICAgR2VuX1NjaWVudGlmaWNOYW1lLAogICAgICAgICBDb3VudCkgJT4lCiAgZmlsdGVyKCFHZW5fU2NpZW50aWZpY05hbWUgJWluJSByYXJlLmdlbnVzKSAlPiUgIyByZW1vdmUgcmFyZSB0YXhhCiAgdW5ncm91cCgpICU+JQogIHN1bW1hcmlzZShBYnVuZGFuY2UgPSAoc3VtKENvdW50KSAvIG5fZGlzdGluY3QoRXZlbnRJRCkpLCAuYnk9YyhWaXNpdElELCBHZW5fU2NpZW50aWZpY05hbWUpKQoKIyMgU3BlY2llcyBhYnVuZGFuY2UgcGVyIHZpc2l0CiMgU2F2ZSBhbiBvYmplY3Qgb2YgYWxsIHRoZSByYXJlIHNwZWNpZXMKcmFyZS5zcGVjaWVzID0gdW5pcXVlKHJhcmUuc3AkU3BfU2NpZW50aWZpY05hbWUpICU+JSBhcy5jaGFyYWN0ZXIoKQojIENyZWF0ZSBhIHRpYmJsZSBvZiBzcGVjaWVzIGFidW5kYW5jZSBleGNsdWRpbmcgcmFyZSBzcGVjaWVzCmNhdGNoLnNwID0gbGVmdF9qb2luKHNlbGVjdChldmVudHMsIFZpc2l0SUQsIEV2ZW50SUQpLCBjYXRjaC4zLCBieSA9ICJFdmVudElEIikgJT4lCiAgc2VsZWN0KFZpc2l0SUQsCiAgICAgICAgIEV2ZW50SUQsCiAgICAgICAgIFNwX1NjaWVudGlmaWNOYW1lLAogICAgICAgICBDb3VudCkgJT4lCiAgZmlsdGVyKCFTcF9TY2llbnRpZmljTmFtZSAlaW4lIHJhcmUuc3BlY2llcykgJT4lICMgcmVtb3ZlIHJhcmUgdGF4YQogIHVuZ3JvdXAoKSAlPiUKICBzdW1tYXJpc2UoQWJ1bmRhbmNlID0gKHN1bShDb3VudCkgLyBuX2Rpc3RpbmN0KEV2ZW50SUQpKSwgLmJ5ID0gYyhWaXNpdElELCBTcF9TY2llbnRpZmljTmFtZSkpCmBgYAoKSnVzdCBieSBsb29raW5nIGF0IHRoZSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIChyb3dzKSBpbiB0aGUgZGF0YSwgSSB0aGluayB3ZSBtYXkgZW5kIHVwIGNvbmR1Y3RpbmcgYW5hbHlzZXMgYXQgdGhlIGdlbnVzIGxldmVsOgoKLSAgIGByIG5yb3coY2F0Y2guZmFtKWAgb2JzZXJ2YXRpb25zIGF0IHRoZSBmYW1pbHkgbGV2ZWwKCi0gICBgciBucm93KGNhdGNoLmdlbilgIG9ic2VydmF0aW9ucyBhdCB0aGUgZ2VudXMgbGV2ZWwKCi0gICBgciBucm93KGNhdGNoLnNwKWAgb2JzZXJ2YXRpb25zIGF0IHRoZSBzcGVjaWVzIGxldmVsCgpMYXN0IHRoaW5nIHdlJ2xsIGRvIGlzIHJlbmFtZSBvdXIgY2F0Y2ggdGliYmxlIGFuZCBjbGVhbiB1cCBvdXIgZW52aXJvbm1lbnQuCgpgYGB7cn0KIyBSZS1hc3NpZ24gd29ya2luZyB2ZXJzaW9uIG9mIGNhdGNoIHRvIGEgY2xlYW5lciBuYW1lCmFzc2lnbigiY2F0Y2giLCBjYXRjaC4zKQpgYGAKCiMjIENsZWFuIHVwCgpgYGB7cn0KIyBDbGVhbiB1cCBvdXIgZW52aXJvbm1lbnQgZm9yIHRoZSBuZXh0IHN0ZXA6CnJtKGxpc3QgPSBscygpWyFscygpICVpbiUgYygnZGF0YScsICdldmVudHMnLCAnY2F0Y2gnLCAnY2F0Y2guZmFtJywgJ2NhdGNoLmdlbicsICdjYXRjaC5zcCcsICdyYXJlLmZhbWlsaWVzJywgJ3JhcmUuZ2VudXMnLCAncmFyZS5zcGVjaWVzJyldKQoKIyBSZXNldCBkaXJlY3RvcnkKd2QgPSBoZXJlKCkKZGlycyA9IHdkICU+JSBsaXN0LmZpbGVzKCkgJT4lIHN0cl9zdWJzZXQocGF0dGVybiA9ICJeUkVBRE1FfF5MSUNFTlNFfC5tZCR8LlJwcm9qJCIsIG5lZ2F0ZSA9IFRSVUUpCmZvciAoaSBpbiBzZXFfYWxvbmcoZGlycykpIHsKICBuYW1lID0gc3RyX3JlcGxhY2VfYWxsKGRpcnNbaV0sICJeIiwgImRpci4iKQogIHBhdGggPSBzdHJfcmVwbGFjZV9hbGwoZGlyc1tpXSwgIl4iLCBzdHJfYyh3ZCwgIi8iKSkKICBhc3NpZ24obmFtZSwgcGF0aCkKICBybShwYXRoLCBpKQp9CmBgYAoKIyBXcmFuZ2xpbmcgdmlzaXQgZGF0YQoKIyMgU2V0IHVwCgpSZW1lbWJlciB0aGF0IG91ciBzYW1wbGVzIG9wZXJhdGUgYXQgdGhlIHZpc2l0IGxldmVsLCB3aGVyZSB1bmlxdWUgZXZlbnRzIHJlcHJlc2VudCByZXBsaWNhdGVzIHdpdGhpbiBlYWNoIHNhbXBsZS4gTGV0J3MgY3JlYXRlIGEgbmV3IHRpYmJsZSB3aGVyZSBlYWNoIHJvdyByZXByZXNlbnRzIGEgc2FtcGxlLiBXZSdsbCB3YW50IHRvIGtlZXAgdHJhY2sgb2YgaG93IG1hbnkgcmVwbGljYXRlcyBlYWNoIHNhbXBsZSBjb250YWlucyBzbyB0aGF0IHdlIG1heSBhY2NvdW50IGZvciB0aGlzIGluIGZ1dHVyZSBhbmFseXNlcy4KCmBgYHtyfQojIE5ldyB0aWJibGUgZm9yIHZpc2l0IGxldmVsIHdyYW5nbGluZywKdmlzaXRzLjEgPSBzZWxlY3QoZXZlbnRzLCBWaXNpdElELCBFdmVudElEKSAlPiUKICBzdW1tYXJpc2UoUmVwbGljYXRlcyA9IG5fZGlzdGluY3QoRXZlbnRJRCksIC5ieSA9IFZpc2l0SUQpICU+JQogIHJpZ2h0X2pvaW4oc2VsZWN0KGV2ZW50cywgLVNpdGVJRCwgLUV2ZW50SUQpLCBieSA9ICJWaXNpdElEIikgJT4lCiAgZGlzdGluY3QoKQp2aXNpdHMuMSAlPiUgbnJvdygpCmBgYAoKIyMgU2tpbW1pbmcgdmlzaXRzCgpUaGVyZSBhcmUgc3VwcG9zZWQgdG8gYmUgYHIgdmlzaXRzLjEkVmlzaXRJRCAlPiUgbl9kaXN0aW5jdCgpYCB2aXNpdHMsIGJ1dCB3ZSBzZWUgbW9yZSByb3dzIHRoYW4gdGhhdCBpbiBvdXIgZGF0YXNldC4gV2hlbiB3ZSBtZXJnZWQgc2l0ZXMgdG9nZXRoZXIsIHRoZXJlIG11c3QndmUgYmVlbiBzYW1wbGVzIHRoYXQgY29udGFpbmVkIG11dGxpcGxlIGVudHJpZXMgZm9yIHNvbWUgb2Ygb3VyIHZhcmlhYmxlcyAoZS5nLiwgbW9yZSB0aGFuIG9uZSBMb2NhdGlvbiBvciBIYWJpdGF0KS4gTGV0J3Mgc2tpbSB0aGUgZGF0YSB0byByZW1pbmQgb3Vyc2VsdmVzIG9mIG91ciB2YXJpYWJsZXM6CgpgYGB7cn0Kc2tpbSh2aXNpdHMuMSkKYGBgCgpSaWdodCBhd2F5LCB3ZSBjYW4gcnVsZSBvdXQgdGhlIHZhcmlhYmxlcyB0aGF0IHdlJ3ZlIGFscmVhZHkgZGVhbHQgd2l0aCBhbmQga25vdyBhcmUgdW5pcXVlIGF0IHRoZSB2aXNpdCBsZXZlbDogVmlzaXRJRCwgTGF0L0xvbiwgRGF0ZSwgYW5kIENsdXN0ZXIuCgpXZSBjYW4gYWxzbyBzaW1wbHkgZHJvcCB0aGUgaW5mb3JtYXRpb24gdGhhdCB3ZSBkb24ndCBuZWVkIHRvIGtlZXAgaW4gdGhpcyB2ZXJzaW9uIG9mIHRoZSBkYXRhLCBuYW1lbHkgUHJvamVjdE5hbWUsIFBvaW50T2ZDb250YWN0LCBhbmQgR2VhciBTcGVjaWZpYy4gSSB0aGluayB3ZSBjYW4gYWxzbyByZW1vdmUgdGhlIFRpZGFsU3RhZ2UgcGFyYW1ldGVyLCBiZWNhdXNlIEkgZG9uJ3QgY2FyZSB0byBhY2NvdW50IGZvciBpdCBhdCB0aGlzIHBvaW50LgoKYGBge3J9CiMgVXBkYXRlIHRpYmJsZSB0byByZW1vdmUgdW5uZWNlc3Nhcnkgc2l0ZSBpbmZvLAp2aXNpdHMuMiA9IHNlbGVjdCh2aXNpdHMuMSwgLWMoUHJvamVjdE5hbWUsIFBvaW50T2ZDb250YWN0LCBHZWFyU3BlY2lmaWMsIFRpZGFsU3RhZ2UpKSAlPiUgZGlzdGluY3QoKQp2aXNpdHMuMiAlPiUgbnJvdygpCmBgYAoKU3RpbGwgbm90IHF1aXRlIHRoZXJlLiBJIGJldCBhIGxvdCBvZiB2aXNpdHMgY29udGFpbiBtdWx0aXBsZSB0ZW1wcywgc2FsaW5pdHksIGhhYml0YXRzLCBhbmQgcHJvYmFibHkgbG9jYXRpb25zLiBXZSBtaWdodCB3YW50IHRvIHVzZSB0aG9zZSBsYXRlciBvbiwgYnV0IGZvciBub3cgd2UgY2FuIGp1c3QgZHJvcCB0aGVtIGJlY2F1c2UgYWxsIHRoZWlyIGluZm8gaXMgY29udGFpbmVkIGluIG91ciBldmVudCBsZXZlbCBkYXRhIG9iamVjdDoKCmBgYHtyfQojIFVwZGF0ZSB0aWJibGUgdG8gcmVtb3ZlICdwcm9ibGVtJyB2YXJpYWJsZXMsCnZpc2l0cy4zID0gc2VsZWN0KHZpc2l0cy4yLCAtYyhSZWdpb24sIExvY2F0aW9uLCBIYWJpdGF0LCBUZW1wZXJhdHVyZSA9IFRlbXBfQywgU2FsaW5pdHkpKSAlPiUgZGlzdGluY3QoKQp2aXNpdHMuMyAlPiUgbnJvdygpCmBgYAoKUHJldHR5IGRhcm4gY2xvc2UuIEhvcGVmdWxseSB3ZSBkb24ndCBoYXZlIHNhbXBsZXMgd2l0aCBtdWx0aXBsZSBtZXNoIHNpemVzLi4uIChzcG9pbGVyKSB3ZSBkby4gWXVjay4KCiMjIFRoZSBtdWx0aXBsZSBnZWFyLXR5cGUgcHJvYmxlbQoKRmlyc3QsIHdlIGlzb2xhdGUgdGhlIGNhc2VzIHdoZXJlIHNpbmd1bGFyIHZpc2l0cyBjb250YWluIHJlcGxpY2F0ZXMgb2YgZGlmZmVyaW5nIG1lc2ggc2l6ZXM6CgpgYGB7cn0KIyBOZXcgb2JqZWN0IGZvciB2aXNpdHMgd2l0aCBtdWx0aXBsZSBtZXNoIHNpemUKbXVsdC5tZXNoID0gc2VsZWN0KHZpc2l0cy4zLCBWaXNpdElELCBNZXNoU2l6ZSkgJT4lCiAgc3VtbWFyaXNlKGBuIE1lc2ggU2l6ZXNgID0gbl9kaXN0aW5jdChNZXNoU2l6ZSksIC5ieSA9IFZpc2l0SUQpICU+JQogIGZpbHRlcihgbiBNZXNoIFNpemVzYCA+IDEpCiMgVmlldwptdWx0Lm1lc2gKYGBgCgpEYW1uLiBMZXQncyBnZXQgYSBiZXR0ZXIgaWRlYSBvZiBob3cgZGlmZmVyZW50IHRoZXNlIG1lc2ggc2l6ZXMgYXJlLCBhbmQgd2hhdCBkYXRhIHNldHMgdGhleSBjb21lIGZyb206CgpgYGB7cn0KIyBKb2luIG11bHRpcGxlIG1lc2ggZXZlbnRzIHdpdGggb3RoZXIgZXZlbnQgbGV2ZWwgaW5mbywKbXVsdC5tZXNoLmV2ZW50cyA9IHNlbGVjdChtdWx0Lm1lc2gsIFZpc2l0SUQpICU+JSBsZWZ0X2pvaW4oZXZlbnRzLCBieSA9ICJWaXNpdElEIikKIyBTa2ltIHRoZSBwcm9qZWN0IGFuZCBkYXRlIGluZm8sCnNlbGVjdChtdWx0Lm1lc2guZXZlbnRzLCBHZWFyU3BlY2lmaWMsIFByb2plY3ROYW1lLCBEYXRlKSAlPiUKICBtdXRhdGUoYWNyb3NzKGMoR2VhclNwZWNpZmljLCBQcm9qZWN0TmFtZSksIH4gYXNfZmFjdG9yKC54KSkpICU+JQogIHNraW0oKQojIFN1bW1hcnkgb2YgZ2VhciBhbmQgbWVzaCBzaXplIHBlciB2aXNpdCwKc2VsZWN0KG11bHQubWVzaC5ldmVudHMsIFZpc2l0SUQsIEdlYXJTcGVjaWZpYywgTWVzaFNpemUpICU+JSAKICBzdW1tYXJpc2Uobl92aXNpdHMgPSBuX2Rpc3RpbmN0KFZpc2l0SUQpLCAuYnkgPSBjKEdlYXJTcGVjaWZpYywgTWVzaFNpemUpKQpgYGAKClRoZSBnb29kIHRoaW5nIGlzIHRoYXQgSSByZWNvZ25pemUgdGhlc2UgZGF0YS4gVGhlIHZpc2l0cyB0aGF0IGNvbnRhaW4gZGlmZmVyZW50IG1lc2ggc2l6ZSBldmVudHMgY29tZSBmcm9tIHRoZSBzYW1lIHByb2plY3Q6IEVQU0NvUiBGaXJlICYgSWNlIEx5bm4gQ2FuYWwgZ3JvdXAgaW4gU291dGhlYXN0IEFLLiBUaGV5IHJlcG9ydGVkIHRoYXQgZGlmZmVyZW50IG1lc2ggc2l6ZXMgaGFkIGFuIGVmZmVjdCBvbiBjb21tdW5pdHkgY29tcG9zaXRpb24gdXNpbmcgc2lkZS1ieS1zaWRlIGNvbXBhcmlzb24gb2YgZ2VhciB0eXBlcyBpbiB0aGVpciBtZXRob2RzIChbTHVuZHN0cm9tIGV0IGFsLiAyMDIyXShodHRwczovL2RvaS5vcmcvMTAuMTAwNy9zMTIyMzctMDIyLTAxMDU3LXgpKS4gVGhpcyBleHBsYWlucyB3aHkgdGhlIGNsdXN0ZXIgYW5kIGdyb3VwaW5nIHN0ZXBzIGNvbWJpbmVkIHRoZSBldmVudHMuCgpUaGUgYmFkIG5ld3MgaXMgdGhhdCB3ZSdsbCBuZWVkIHRvIHJlLWRvIHByZXZpb3VzIHdyYW5nbGluZyBzdGVwcyBvbiB3aGljaCBtZXNoIHNpemUgaXMgZGVwb2VuZGVudC4gVGhpcyB3aWxsIGludm9sdmUgc2VwYXJhdGluZyB0aGUgYWZmZWN0ZWQgVmlzaXRJRCdzIGludG8gc2VwYXJhdGUgc2FtcGxlcyBiYXNlZCBvbiBtZXNoIHNpemUsIHdoaWNoIHdpbGwgcmVzdWx0IGluIGFib3V0IDEwMCBtb3JlIHVuaXF1ZSBWaXNpdElEJ3MgYW5kIGRpZmZlcmVudCBtZWFuIGFidW5kYW5jZSBhc3NvY2lhdGVkIGNhdGNoZXMuCgpXZSdsbCBuZWVkIHRvIHN0YXJ0IGJ5IHVwZGF0aW5nIG91ciBldmVudHMgbGV2ZWwgZGF0YSBvYmplY3QuIFNpbmNlIHRoaXMgaW52b2x2ZXMgb3ZlcndyaXRpbmcgYSBtYWpvciBkYXRhIHN0YWdlLCBJJ2xsIGJlIGV4cGxpY2l0IGluIHdoYXQgSSdtIGRvaW5nOgoKYGBge3J9CiMgQ3JlYXRlIGEgdGliYmxlIHRvIHN0b3JlIHRoZSBWaXNpdElEJ3MgdG8gYmUgZml4ZWQKbXVsdC5tZXNoLmZpeCA9IHNlbGVjdChtdWx0Lm1lc2guZXZlbnRzLCBWaXNpdElELCBFdmVudElELCBHZWFyU3BlY2lmaWMsIE1lc2hTaXplKSAlPiUKICAjIENyZWF0ZSBhIHVuaXF1ZSBzdWZmaXggdGhhdCBpZGVudGlmaWVzIG1lc2ggc2l6ZSB1c2VkIHBlciBldmVudAogIG11dGF0ZShzdWZmaXggPSBnc3ViKCJCU0VJTkUtIiwgIiIsIEdlYXJTcGVjaWZpYykpICU+JQogICMgQWRkIHRoZSBzdWZmaXggdG8gVmlzaXRJRCwgY3JlYXRpbmcgbmV3IFZpc2l0SUQncwogIHVuaXRlKGNvbCA9IFZpc2l0SURfbmV3LCBWaXNpdElELCBzdWZmaXgsIHNlcCA9ICJfIikgJT4lCiAgIyBSZXRhaW4gb25seSB0aGUgVmlzaXRJRCdzIGFuZCB0aGVpciBFdmVudElEJ3MKICBzZWxlY3QoRXZlbnRJRCwgVmlzaXRJRF9uZXcpCgojIFVwZGF0ZSB0aGUgJ2V2ZW50cycgdGliYmxlIHdpdGggZml4ZWQgVmlzaXRJRCdzLApldmVudHMgPSBsZWZ0X2pvaW4oc2VsZWN0KGV2ZW50cywgRXZlbnRJRCwgVmlzaXRJRCksIG11bHQubWVzaC5maXgsIGJ5ID0gIkV2ZW50SUQiKSAlPiUKICAjIFdoZXJlIFZpc2l0SUQncyB3ZXJlIG5vdCBmaXhlZCwgZmlsbCBpbiB3aXRoIG9sZCBWaXNpdElEJ3MKICBtdXRhdGUoVmlzaXRJRF9uZXcgPSBpZmVsc2UoaXMubmEoVmlzaXRJRF9uZXcpLCBWaXNpdElELCBWaXNpdElEX25ldykpICU+JQogICMgUmVtb3ZlIG9sZCBWaXNpdElEIGNvbHVtbgogIHNlbGVjdCgtVmlzaXRJRCkgJT4lCiAgIyBSZS1qb2luIGV2ZW50cyBkYXRhICh3L28gdGhlIG9sZCBWaXNpdElEJ3MpIHdpdGggdGhlIG5ldyBWaXNpdElEJ3MsCiAgbGVmdF9qb2luKHNlbGVjdChldmVudHMsIC1WaXNpdElEKSwgYnkgPSBjKCJFdmVudElEIikpICU+JQogICMgUmVuYW1lICh0aGlzIGNvdWxkIGJlIGRvbmUgZWFybGllciBidXQgc2VlbXMgY2xlYXJlciBoZXJlKQogIHJlbmFtZShWaXNpdElEID0gVmlzaXRJRF9uZXcpCmBgYAoKTmV4dCwgd2UgbmVlZCB0byByZS1ydW4gdGhlIGNhdGNoIGFuZCB2aXNpdHMgc3RlcHMgdGhhdCBhcmUgYmFzZWQgb24gdGhlIGV2ZW50cyBkYXRhLgoKIyMgUmUtY3JlYXRpbmcgY2F0Y2ggc3VtbWFyaWVzIHsudGFic2V0IC50YWJzZXQtcGlsbHN9CgpXZSdsbCBuZWVkIHRvIHJlZG8gdGhlIGFidW5kYW5jZSBhbmQgb2NjdXJyZW5jZSBjYWxjdWxhdGlvbnMgYmVjYXVzZSB0aGUgbnVtYmVyIG9mIHZpc2l0cyBoYXMgY2hhbmdlZC4gUmFyZSB0YXhhIHdpbGwgc3RpbGwgYmUgZGVmaW5lZCBhcyB0aG9zZSBvY2N1cnJpbmcgaW4gbGVzcyB0aGFuIDUgdmlzaXRzIChzdGlsbCBlcXVpdmFsZW50IHRvIDwwLjI1JSkuIFRoZSBmcmVxdWVuY3kgYW5kIGFidW5kYW5jZSBwbG90cyBhcmUgbm90IGFsbCB0aGF0IGRpZmZlcmVudCwgYnV0IEkgc2hvdyB1cGRhdGVkIGNhdGNoIHN1bW1hcnkgdGFibGVzIGJlbG93IGZvciBlYWNoIHRheGEgbGV2ZWwuIE5vdGUgdGhhdCBJIGhpZ2hsaWdodCByYXJlIHRheGEgaW4gb3JhbmdlLgoKIyMjIEZhbWlseSB7LnVubnVtYmVyZWR9CgpgYGB7ciBlY2hvID0gRkFMU0V9CiMjIFJlLXN1bW1hcml6ZSBjYXRjaAojIEFidW5kYW5jZQpmYW0uYWJ1biA9IHN1bW1hcmlzZShjYXRjaCwgQWJ1bmRhbmNlID0gc3VtKENvdW50KSwgLmJ5ID0gRmFtX1NjaWVudGlmaWNOYW1lKQojIEZyZXF1ZW5jeSBvZiBvY2N1cnJlbmNlCmZhbS5mcmVxLm9jY3VyID0gbGVmdF9qb2luKGNhdGNoLCBzZWxlY3QoZXZlbnRzLCBWaXNpdElELCBFdmVudElEKSwgYnkgPSAiRXZlbnRJRCIpICU+JQogIHN1bW1hcmlzZShQcmVzZW5jZSA9IG5fZGlzdGluY3QoRmFtX1NjaWVudGlmaWNOYW1lKSwgLmJ5ID0gYyhWaXNpdElELCBGYW1fU2NpZW50aWZpY05hbWUpKSAlPiUKICBzdW1tYXJpc2UoT2NjdXJyZW5jZSA9IHN1bShQcmVzZW5jZSksIC5ieSA9IEZhbV9TY2llbnRpZmljTmFtZSkgJT4lCiAgbXV0YXRlKFBlcmNfT2NjdXJyZW5jZSA9IHJvdW5kKE9jY3VycmVuY2UgLyBuX2Rpc3RpbmN0KGV2ZW50cyRWaXNpdElEKSAqIDEwMCwgMikpCiMgQ29tYmluZSBjYWxjdWxhdGVkIHZhcmlhYmxlcyBhbmQgZGVmaW5lIHJhcmUgdGF4YQpmYW0udGJsID0gZnVsbF9qb2luKGZhbS5hYnVuLCBmYW0uZnJlcS5vY2N1ciwgYnkgPSAnRmFtX1NjaWVudGlmaWNOYW1lJykgJT4lCiAgbXV0YXRlKFJhcmUgPSBpZmVsc2UoT2NjdXJyZW5jZSA8IDUsICJZZXMiLCAiTm8iKSkKIyBTYXZlIHJhcmUgdGF4YSBhcyB2ZWN0b3IKcmFyZS5mYW1pbGllcyA9IGZpbHRlcihmYW0udGJsLCBSYXJlID09ICJZZXMiKSAlPiUgcHVsbChGYW1fU2NpZW50aWZpY05hbWUpCiMgU3VtbWFyeSB0YWJsZQpmYW0udGJsICU+JQogIHNlbGVjdChgU2NpZW50aWZpYyBOYW1lYCA9IEZhbV9TY2llbnRpZmljTmFtZSwgYFBlcmNlbnQgT2NjdXJyZW5jZWAgPSBQZXJjX09jY3VycmVuY2UsIEFidW5kYW5jZSwgUmFyZSkgJT4lCiAgYXJyYW5nZShhcy5jaGFyYWN0ZXIoYFNjaWVudGlmaWMgTmFtZWApKSAlPiUKICBkYXRhdGFibGUocm93bmFtZXMgPSBGQUxTRSwKICAgICAgICAgICAgZXh0ZW5zaW9ucyA9ICdTY3JvbGxlcicsCiAgICAgICAgICAgIG9wdGlvbnMgPSBsaXN0KGRlZmVyUmVuZGVyID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgc2Nyb2xsWSA9IDUwMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgc2Nyb2xsZXIgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5EZWZzID0gbGlzdChsaXN0KHRhcmdldHMgPSAnUmFyZScsIHZpc2libGUgPSBGQUxTRSkpKSkgJT4lIAogIGZvcm1hdFN0eWxlKGNvbHVtbnMgPSAiUmFyZSIsCiAgICAgICAgICAgICAgdGFyZ2V0ID0gInJvdyIsCiAgICAgICAgICAgICAgYmFja2dyb3VuZENvbG9yID0gc3R5bGVFcXVhbCgiWWVzIiwgImdvbGRlbnJvZCIpKQpgYGAKCiMjIyBHZW51cyB7LnVubnVtYmVyZWR9CgpgYGAge3IgZWNobyA9IEZBTFNFfQojIyBSZS1zdW1tYXJpemUgY2F0Y2gKIyBBYnVuZGFuY2UKZ2VuLmFidW4gPSBzdW1tYXJpc2UoY2F0Y2gsIEFidW5kYW5jZSA9IHN1bShDb3VudCksIC5ieSA9IEdlbl9TY2llbnRpZmljTmFtZSkKIyBGcmVxdWVuY3kgb2Ygb2NjdXJyZW5jZQpnZW4uZnJlcS5vY2N1ciA9IGxlZnRfam9pbihjYXRjaCwgc2VsZWN0KGV2ZW50cywgVmlzaXRJRCwgRXZlbnRJRCksIGJ5ID0gIkV2ZW50SUQiKSAlPiUKICBzdW1tYXJpc2UoUHJlc2VuY2UgPSBuX2Rpc3RpbmN0KEdlbl9TY2llbnRpZmljTmFtZSksIC5ieSA9IGMoVmlzaXRJRCwgR2VuX1NjaWVudGlmaWNOYW1lKSkgJT4lCiAgc3VtbWFyaXNlKE9jY3VycmVuY2UgPSBzdW0oUHJlc2VuY2UpLCAuYnkgPSBHZW5fU2NpZW50aWZpY05hbWUpICU+JQogIG11dGF0ZShQZXJjX09jY3VycmVuY2UgPSByb3VuZChPY2N1cnJlbmNlIC8gbl9kaXN0aW5jdChldmVudHMkVmlzaXRJRCkgKiAxMDAsIDIpKQojIENvbWJpbmUgY2FsY3VsYXRlZCB2YXJpYWJsZXMgYW5kIGRlZmluZSByYXJlIHRheGEKZ2VuLnRibCA9IGZ1bGxfam9pbihnZW4uYWJ1biwgZ2VuLmZyZXEub2NjdXIsIGJ5ID0gJ0dlbl9TY2llbnRpZmljTmFtZScpICU+JQogIG11dGF0ZShSYXJlID0gaWZlbHNlKE9jY3VycmVuY2UgPCA1LCAiWWVzIiwgIk5vIikpCiMgU2F2ZSByYXJlIHRheGEgYXMgdmVjdG9yCnJhcmUuZ2VudXMgPSBmaWx0ZXIoZ2VuLnRibCwgUmFyZSA9PSAiWWVzIikgJT4lIHB1bGwoR2VuX1NjaWVudGlmaWNOYW1lKQojIFN1bW1hcnkgdGFibGUKZ2VuLnRibCAlPiUKICBzZWxlY3QoYFNjaWVudGlmaWMgTmFtZWAgPSBHZW5fU2NpZW50aWZpY05hbWUsIGBQZXJjZW50IE9jY3VycmVuY2VgID0gUGVyY19PY2N1cnJlbmNlLCBBYnVuZGFuY2UsIFJhcmUpICU+JQogIGFycmFuZ2UoYXMuY2hhcmFjdGVyKGBTY2llbnRpZmljIE5hbWVgKSkgJT4lCiAgZGF0YXRhYmxlKHJvd25hbWVzID0gRkFMU0UsCiAgICAgICAgICAgIGV4dGVuc2lvbnMgPSAnU2Nyb2xsZXInLAogICAgICAgICAgICBvcHRpb25zID0gbGlzdChkZWZlclJlbmRlciA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjcm9sbFkgPSA1MDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjcm9sbGVyID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sdW1uRGVmcyA9IGxpc3QobGlzdCh0YXJnZXRzID0gJ1JhcmUnLCB2aXNpYmxlID0gRkFMU0UpKSkpICU+JSAKICBmb3JtYXRTdHlsZShjb2x1bW5zID0gIlJhcmUiLAogICAgICAgICAgICAgIHRhcmdldCA9ICJyb3ciLAogICAgICAgICAgICAgIGJhY2tncm91bmRDb2xvciA9IHN0eWxlRXF1YWwoIlllcyIsICJnb2xkZW5yb2QiKSkKYGBgCgojIyMgU3BlY2llcyB7LnVubnVtYmVyZWR9CgpgYGAge3IgZWNobyA9IEZBTFNFfQojIyBSZS1zdW1tYXJpemUgY2F0Y2gKIyBBYnVuZGFuY2UKc3AuYWJ1biA9IHN1bW1hcmlzZShjYXRjaCwgQWJ1bmRhbmNlID0gc3VtKENvdW50KSwgLmJ5ID0gU3BfU2NpZW50aWZpY05hbWUpCiMgRnJlcXVlbmN5IG9mIG9jY3VycmVuY2UKc3AuZnJlcS5vY2N1ciA9IGxlZnRfam9pbihjYXRjaCwgc2VsZWN0KGV2ZW50cywgVmlzaXRJRCwgRXZlbnRJRCksIGJ5ID0gIkV2ZW50SUQiKSAlPiUKICBzdW1tYXJpc2UoUHJlc2VuY2UgPSBuX2Rpc3RpbmN0KFNwX1NjaWVudGlmaWNOYW1lKSwgLmJ5ID0gYyhWaXNpdElELCBTcF9TY2llbnRpZmljTmFtZSkpICU+JQogIHN1bW1hcmlzZShPY2N1cnJlbmNlID0gc3VtKFByZXNlbmNlKSwgLmJ5ID0gU3BfU2NpZW50aWZpY05hbWUpICU+JQogIG11dGF0ZShQZXJjX09jY3VycmVuY2UgPSByb3VuZChPY2N1cnJlbmNlIC8gbl9kaXN0aW5jdChldmVudHMkVmlzaXRJRCkgKiAxMDAsIDIpKQojIENvbWJpbmUgY2FsY3VsYXRlZCB2YXJpYWJsZXMgYW5kIGRlZmluZSByYXJlIHRheGEKc3AudGJsID0gZnVsbF9qb2luKHNwLmFidW4sIHNwLmZyZXEub2NjdXIsIGJ5ID0gJ1NwX1NjaWVudGlmaWNOYW1lJykgJT4lCiAgbXV0YXRlKFJhcmUgPSBpZmVsc2UoT2NjdXJyZW5jZSA8IDUsICJZZXMiLCAiTm8iKSkKIyBTYXZlIHJhcmUgdGF4YSBhcyB2ZWN0b3IKcmFyZS5zcGVjaWVzID0gZmlsdGVyKHNwLnRibCwgUmFyZSA9PSAiWWVzIikgJT4lIHB1bGwoU3BfU2NpZW50aWZpY05hbWUpCiMgU3VtbWFyeSB0YWJsZQpzcC50YmwgJT4lCiAgc2VsZWN0KGBTY2llbnRpZmljIE5hbWVgID0gU3BfU2NpZW50aWZpY05hbWUsIGBQZXJjZW50IE9jY3VycmVuY2VgID0gUGVyY19PY2N1cnJlbmNlLCBBYnVuZGFuY2UsIFJhcmUpICU+JQogIGFycmFuZ2UoYXMuY2hhcmFjdGVyKGBTY2llbnRpZmljIE5hbWVgKSkgJT4lCiAgZGF0YXRhYmxlKHJvd25hbWVzID0gRkFMU0UsCiAgICAgICAgICAgIGV4dGVuc2lvbnMgPSAnU2Nyb2xsZXInLAogICAgICAgICAgICBvcHRpb25zID0gbGlzdChkZWZlclJlbmRlciA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjcm9sbFkgPSA1MDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjcm9sbGVyID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sdW1uRGVmcyA9IGxpc3QobGlzdCh0YXJnZXRzID0gJ1JhcmUnLCB2aXNpYmxlID0gRkFMU0UpKSkpICU+JSAKICBmb3JtYXRTdHlsZShjb2x1bW5zID0gIlJhcmUiLAogICAgICAgICAgICAgIHRhcmdldCA9ICJyb3ciLAogICAgICAgICAgICAgIGJhY2tncm91bmRDb2xvciA9IHN0eWxlRXF1YWwoIlllcyIsICJnb2xkZW5yb2QiKSkKYGBgCgojIyBSZS1jYWxjdWxhdGluZyBhYnVuZGFuY2UgcGVyIHZpc2l0CgpTaW1pbGFybHksIHdlJ2xsIHdhbnQgdG8gdG8gcmVkbyBvdXIgdmlzaXQgbWVhbiBhYnVuZGFuY2VzICh0YXhhIHRvdGFsIC8gIyBvZiBldmVudHMpLiBUaGUgYmVsb3cgY29kZSBpcyBwcmV0dHkgbXVjaCB0aGUgc2FtZSBhcyBwcmV2aW91c2x5IHJ1biwgZXhjZXB0IHRoYXQgSSBjaGFuZ2UgdGhlIG9iamVjdCBuYW1lIHRvIGJldHRlciBkaXN0aW5ndWlzaCB0aGlzIHN1YnNldCBmcm9tIG90aGVyIG9iamVjdHMgaW4gb3VyIGVudmlyb25tZW50LgoKYGBgIHtyfQojIENyZWF0ZSBhIHRpYmJsZSBvZiBtZWFuIGFidW5kYW5jZSBwZXIgdmlzaXQgZm9yIGVhY2ggZmFtaWx5IGV4Y2x1ZGluZyByYXJlIHRheGEKYWJ1bi5mYW0gPSBsZWZ0X2pvaW4oc2VsZWN0KGV2ZW50cywgVmlzaXRJRCwgRXZlbnRJRCksIGNhdGNoLCBieSA9ICJFdmVudElEIikgJT4lCiAgc2VsZWN0KFZpc2l0SUQsCiAgICAgICAgIEV2ZW50SUQsCiAgICAgICAgIEZhbV9TY2llbnRpZmljTmFtZSwKICAgICAgICAgQ291bnQpICU+JQogIGZpbHRlcighRmFtX1NjaWVudGlmaWNOYW1lICVpbiUgcmFyZS5mYW1pbGllcykgJT4lCiAgdW5ncm91cCgpICU+JQogIHN1bW1hcmlzZShBYnVuZGFuY2UgPSAoc3VtKENvdW50KSAvIG5fZGlzdGluY3QoRXZlbnRJRCkpLCAuYnkgPSBjKFZpc2l0SUQsIEZhbV9TY2llbnRpZmljTmFtZSkpCgojIENyZWF0ZSBhIHRpYmJsZSBvZiBtZWFuIGFidW5kYW5jZSBwZXIgdmlzaXQgZm9yIGVhY2ggR2VudXMgZXhjbHVkaW5nIHJhcmUgdGF4YQphYnVuLmdlbiA9IGxlZnRfam9pbihzZWxlY3QoZXZlbnRzLCBWaXNpdElELCBFdmVudElEKSwgY2F0Y2gsIGJ5ID0gIkV2ZW50SUQiKSAlPiUKICBzZWxlY3QoVmlzaXRJRCwKICAgICAgICAgRXZlbnRJRCwKICAgICAgICAgR2VuX1NjaWVudGlmaWNOYW1lLAogICAgICAgICBDb3VudCkgJT4lCiAgZmlsdGVyKCFHZW5fU2NpZW50aWZpY05hbWUgJWluJSByYXJlLmdlbnVzKSAlPiUgIyByZW1vdmUgcmFyZSB0YXhhCiAgdW5ncm91cCgpICU+JQogIHN1bW1hcmlzZShBYnVuZGFuY2UgPSAoc3VtKENvdW50KSAvIG5fZGlzdGluY3QoRXZlbnRJRCkpLCAuYnkgPSBjKFZpc2l0SUQsIEdlbl9TY2llbnRpZmljTmFtZSkpCgojIENyZWF0ZSBhIHRpYmJsZSBvZiBtZWFuIGFidW5kYW5jZSBwZXIgdmlzaXQgZm9yIGVhY2ggU3BlY2llcyBleGNsdWRpbmcgcmFyZSB0YXhhCmFidW4uc3AgPSBsZWZ0X2pvaW4oc2VsZWN0KGV2ZW50cywgVmlzaXRJRCwgRXZlbnRJRCksIGNhdGNoLCBieSA9ICJFdmVudElEIikgJT4lCiAgc2VsZWN0KFZpc2l0SUQsCiAgICAgICAgIEV2ZW50SUQsCiAgICAgICAgIFNwX1NjaWVudGlmaWNOYW1lLAogICAgICAgICBDb3VudCkgJT4lCiAgZmlsdGVyKCFTcF9TY2llbnRpZmljTmFtZSAlaW4lIHJhcmUuc3BlY2llcykgJT4lICMgcmVtb3ZlIHJhcmUgdGF4YQogIHVuZ3JvdXAoKSAlPiUKICBzdW1tYXJpc2UoQWJ1bmRhbmNlID0gKHN1bShDb3VudCkgLyBuX2Rpc3RpbmN0KEV2ZW50SUQpKSwgLmJ5ID0gYyhWaXNpdElELCBTcF9TY2llbnRpZmljTmFtZSkpCmBgYAoKTm93IHdlIGNhbiByZS1ydW4gdGhlIFZpc2l0SUQgd3JhbmdsaW5nIGNvZGUgZnJvbSBlYXJsaWVyIGluIHRoaXMgc2VjdGlvbjoKCmBgYHtyfQojIFJlLXJ1biB2aXNpdHMuMSBjb2RlLAp2aXNpdHMuNCA9IHNlbGVjdChldmVudHMsIFZpc2l0SUQsIEV2ZW50SUQpICU+JQogIHN1bW1hcmlzZShSZXBsaWNhdGVzID0gbl9kaXN0aW5jdChFdmVudElEKSwgLmJ5ID0gVmlzaXRJRCkgJT4lCiAgcmlnaHRfam9pbihzZWxlY3QoZXZlbnRzLCAtU2l0ZUlELCAtRXZlbnRJRCksIGJ5ID0gIlZpc2l0SUQiKSAlPiUKICBkaXN0aW5jdCgpICU+JQogICMgUmUtcnVuIHZpc2l0cy4yICYgdmlzaXRzLjMgY29kZSwKICBzZWxlY3QoLWMoUHJvamVjdE5hbWUsIFBvaW50T2ZDb250YWN0LCBHZWFyU3BlY2lmaWMsIFRpZGFsU3RhZ2UsCiAgICAgICAgICAgIFJlZ2lvbiwgTG9jYXRpb24sIEhhYml0YXQsIFRlbXBlcmF0dXJlID0gVGVtcF9DLCBTYWxpbml0eSkpICU+JQogIGRpc3RpbmN0KCkKYGBgCgpXZSBzaG91bGQgbm93IGhhdmUgYHIgdmlzaXRzLjQkVmlzaXRJRCAlPiUgbl9kaXN0aW5jdCgpYCB1bmlxdWUgdmlzaXRzLiBMZXQncyBkb3VibGUgY2hlY2sgdGhhdCB0aGVyZSBpcyBvbmx5IG9uZSByb3cgcGVyIFZpc2l0SUQ6CgpgYGB7cn0Kbl9kaXN0aW5jdCh2aXNpdHMuNCRWaXNpdElEKSA9PSBucm93KHZpc2l0cy40KQpgYGAKCiMjIFJlLXZpc2l0aW5nIHNhbXBsZSBmcmVxdWVuY3kKCkxldCdzIGFsc28gcmV2aWV3IG91ciBldmVudHMgcGVyIHZpc2l0IGZyZXF1ZW5jeSBwbG90IGJlY2F1c2UgdGhhdCB3aWxsIGhhdmUgY2hhbmdlZCB0b28uCgpgYGB7ciBlY2hvID0gRkFMU0V9CiMgQW5kIGV2ZW50cyBwZXIgdmlzaXQKZXZlbnRzICU+JSBzdW1tYXJpc2UoRXZlbnRzID0gbl9kaXN0aW5jdChFdmVudElEKSwgLmJ5ID0gVmlzaXRJRCkgJT4lCiAgZ2dwbG90KGRhdGEgPSAuLCBhZXMoeCA9IEV2ZW50cykpICsKICBnZW9tX2JhcihhZXMoeSA9IGFmdGVyX3N0YXQoY291bnQpKSkgKwogIGdlb21fdGV4dChzdGF0ID0gJ0NvdW50JywgYWVzKGxhYmVsID0gYWZ0ZXJfc3RhdChjb3VudCkpLCB2anVzdCA9IC0wLjUpICsKICBsYWJzKHRpdGxlID0gIkZyZXF1ZW5jeSBvZiBldmVudHMgcGVyIHNhbXBsZSBhZnRlciBzb2x2aW5nIG11bHRpcGxlIGdlYXItdHlwZSBwcm9ibGVtIikKIyBnZ3NhdmUoIm5mYV9mcmVxLWV2ZW50cy1wZXItc2FtcGxlLnBuZyIsIHBsb3QgPSBsYXN0X3Bsb3QoKSwgZGV2aWNlID0gJ3BuZycsIHBhdGggPSBmaWxlLnBhdGgoZGlyLmZpZ3MpKQpgYGAKClRoZSBkaWZmZXJlbmNlIGlzbid0IHZlcnkgbm90aWNlYWJsZS0gd2UgZW5kIHVwIHdpdGggYSBjZW50cmFsIHRlbmRlbmN5IHRoYXQgaGFzIHNsaWdodGx5IHNoaWZ0ZWQgbGVmdC4KCkxhc3QsIHdlIHNhdmUgb3VyIGZpeGVkIHZpc2l0cyB0aWJibGUgYXMgYSBuZXcgb2JqZWN0IHdpdGggYSBjbGVhbmVyIG5hbWUsIGFuZCB0aGVuIGNsZWFuIHVwIHRoZSBlbnZpcm9ubWVudCBmb3IgbmV4dCBzdGVwcy4KCmBgYHtyfQp2aXNpdHMgPSB2aXNpdHMuNApgYGAKCiMgQ2xlYW4gdXAgYW5kIHNhdmUgd29yawoKYGBge3J9CiMgUmVtb3ZlIHVud2FudGVkIG9iamVjdHMKcm0obGlzdCA9IGxzKClbIWxzKCkgJWluJSBjKCdkYXRhJywgJ2V2ZW50cycsICdjYXRjaCcsICd2aXNpdHMnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgJ2FidW4uZmFtJywgJ2FidW4uZ2VuJywgJ2FidW4uc3AnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgJ2ZhbS50YmwnLCAnZ2VuLnRibCcsICdzcC50YmwnKV0pCgojIENoZWNrIHNpemVzIG9mIHRoZSBkYXRhIG9iamVjdHMKZWFwcGx5KGFzLmVudmlyb25tZW50KC0xKSwgRlVOID0gZnVuY3Rpb24oeCkgZm9ybWF0KG9iamVjdC5zaXplKHgpLCB1bml0cyA9ICJNYiIpKQojIFRoZSBvcmlnaW5hbCAnZGF0YScgaW1wb3J0IGlzIHF1aXRlIGxhcmdlLAojIGJ1dCBhbHNvIHVubmVlZGVkIGJjIHRoZSBpbmZvIGlzIGhlbGQgaW4gb3RoZXIgb2JqZWN0cywgc28gbGV0J3MgcmVtb3ZlIGl0OgpybSgnZGF0YScpCgojIFNhdmUgd2hhdCdzIGxlZnQsCnNhdmUobGlzdCA9IGxzKCksIGZpbGUgPSBmaWxlLnBhdGgoaGVyZSgpLCAiZGF0YSIsICJORkFfd3JhbmdsZWQucmRhIikpCmBgYA==